Compare commits

..

174 Commits

Author SHA1 Message Date
Matthias Mailänder
77e795cfd4 Merge pull request #10244 from abcdefg30/prep-fort-lonestar
Fix and rebalance Fort Lonestar - prep-1512 version
2015-12-24 15:38:42 +01:00
abcdefg30
94f800dc8c Add a "tutorial" message at the beginning of allies05a 2015-12-24 15:09:02 +01:00
abcdefg30
808baa6afd Fix the spawnpoint selection not working on Fort Lonestar 2015-12-21 16:32:41 +01:00
abcdefg30
2a67fb39a3 Remove the unused OilDerricks entry 2015-12-21 16:32:40 +01:00
abcdefg30
fc455ba9e2 Fix snipers not autotargeting 2015-12-21 16:32:39 +01:00
abcdefg30
62baeb38a6 Fix snipers being killable by dogs 2015-12-21 16:32:37 +01:00
abcdefg30
43a98b4cbd Add an "Endless mode" difficulty 2015-12-21 16:32:36 +01:00
abcdefg30
2f57c6c33c Add a difficulties to Fort Lonestar and try to balance them 2015-12-21 16:32:35 +01:00
abcdefg30
47f2a721b3 Add support for sending Iron Curtained units on Fort Lonestar 2015-12-21 16:32:33 +01:00
abcdefg30
1f912d94d2 Make TankNapalm unblockable on Fort Lonestar 2015-12-21 16:32:32 +01:00
abcdefg30
ad383170d5 Increase the damage of the arty on Fort Lonestar 2015-12-21 16:32:30 +01:00
abcdefg30
399f554316 Make MECH buildable on Fort Lonestar but very expensive 2015-12-21 16:32:29 +01:00
abcdefg30
985cd6f7ea Add myself to the Authors of Fort Lonestar 2015-12-21 16:32:28 +01:00
abcdefg30
338093b710 Adjust the final message displayed on Fort Lonestar 2015-12-21 16:32:26 +01:00
abcdefg30
76b94cc7f3 Add support for sending unarmed actors as part of the waves on Fort Lonestar 2015-12-21 16:32:25 +01:00
abcdefg30
885068f89d Clean the yaml up and fix wrong inheritance on Fort Lonestar
Fort Lonestar became unplayable when someone changed the weapon default definitions
As most of its weapons try to override the defaults this resulted in two SpreadDamageWarheads (Warhead: and Warhead@1Dam:) for each weapon
2015-12-21 16:32:24 +01:00
abcdefg30
886ae8786e Let the defenses give buildable area on Fort Lonestar 2015-12-21 16:32:22 +01:00
abcdefg30
6bd430fbc2 Enable AllyBuildRadius on Fort Lonestar 2015-12-21 16:32:21 +01:00
abcdefg30
f0ff1c12ed Add different ProductionBars for each production queue on Fort Lonestar 2015-12-21 16:32:20 +01:00
abcdefg30
02e1cc6172 Improve crates on Fort Lonestar 2015-12-21 16:32:18 +01:00
abcdefg30
c1c1e30203 Remove MustBeDestroyed: from SPY on Fort Lonestar 2015-12-21 16:32:17 +01:00
abcdefg30
83e80cd9fd Add paratroopers to Fort Lonestar 2015-12-21 16:32:16 +01:00
abcdefg30
646ca0f629 Rework the wave system of Fort Lonestar 2015-12-21 16:32:14 +01:00
abcdefg30
02e044dd8b Fix a crash in fort-lonestar.lua 2015-12-21 16:32:13 +01:00
Oliver Brakmann
a1f7e16085 Fix the StartsRevealed option of FrozenUnderFog being ignored 2015-12-20 17:46:19 +01:00
abcdefg30
f94b44574d Document fields in DamagedWithoutFoundationInfo 2015-12-20 18:10:55 +02:00
abcdefg30
2842ea42d7 Fix DamagedWithoutFoundation causing more damage than it should
Tick caused extra damage in addition to the damage in AddedToWorld,
which lead the actor to have less health than damageThreshold.
2015-12-20 18:10:49 +02:00
Oliver Brakmann
0051d0ce08 Fix gap gens being buildable in Soviet 06x 2015-12-20 17:32:15 +02:00
abcdefg30
1c7c329f1d Fix crates not spawning immediately on the dropzone maps 2015-12-19 13:37:27 +00:00
abcdefg30
4d368c1ec1 Merge pull request #10213 from obrakmann/fix9851_prep-edition
[prep] Fix AttackMove not working properly under certain conditions
2015-12-13 20:30:24 +01:00
RoosterDragon
c504b2a9ae Ensure LaysTerrain doesn't access cells outside the map. 2015-12-13 20:07:48 +01:00
RoosterDragon
b9f08b4607 Fix GPS refreshing for incorrect player.
GpsWatcher.RefreshGps was wrongly using the render player in simulation code.
2015-12-13 18:30:57 +01:00
RoosterDragon
5897230226 Fix GPS dot relying on frozen actor render state.
The GPS dot was not displayed if a frozen actor for it was already visible. However, this was mistakenly affecting visibility checks in the world. Now, we only avoid rendering it when the frozen actor is present, but the indicator is still regarded as visible for visilbity checks in the world.
2015-12-13 18:30:51 +01:00
RoosterDragon
f26b0f1251 Ensure frozen actors are rendered on the first tick they become visible.
The previous lazy rendering means the snapshot of the render state might be more up to date then when the frozen actor actually did become visible. Now, we take this snapshot as soon as needed. We still retain the performance of only doing this rendering when needed by avoiding extra rendering until the visibility cycles again.
2015-12-13 18:30:43 +01:00
RoosterDragon
7bc4d205fe Merge dictionaries in GpsDot.
This speeds up GpsDot.Tick as it requires only one dictionary lookup per player rather than two.
2015-12-13 18:30:35 +01:00
Oliver Brakmann
71d4e29483 Fix AttackMove not working properly under certain conditions
This fixes the issue where AttackMove (and possibly other order modes) would not work properly when people were clicking their mouse buttons at the same time. A move order (the default order mode) would be issued instead.
2015-12-13 14:17:39 +01:00
abcdefg30
0e082183e3 Let spawned soviet vehicles attack move on Survival02 2015-12-12 23:50:50 +02:00
RoosterDragon
00bce1266f When hardware cursors fail, retry next session. 2015-12-12 12:56:42 +00:00
RoosterDragon
09f0242059 When the SDL hardware cursor fails, ensure more of the error message is logged.
Capture the useful details from the inner exception into the message, as this will be logged.
2015-12-12 12:56:35 +00:00
Oliver Brakmann
aa14f1b0a2 Fix showing unread messages count on active lobby chat button
The lobby chat button text would change to include the unread messages count for a split second, even when the lobby chat was the current panel, leading to the button text sort of 'blinking' in an ugly way.
2015-12-08 20:51:32 +00:00
abcdefg30
3cf6148c03 Fix a crash when producing an actor without OccupiesSpace in combination with UpgradeActorsNear 2015-12-03 21:57:38 +01:00
Oliver Brakmann
eddda3ecca Set enemies of 'Creeps' player when adding a new player in the editor 2015-11-29 20:49:09 +02:00
abcdefg30
ec001a608e Misc (style) improvements in the upgrade rule code 2015-11-29 20:48:14 +02:00
abcdefg30
07ca72127f Try to avoid adding duplicate values through upgrade rules 2015-11-29 20:48:07 +02:00
abcdefg30
246b25f008 Let upgrade rules also account for negated traits
(this commit affects only "-SpawnViceroid:", "-LeavesHusk:", "-Plane:" and "-Helicopter:")
2015-11-29 20:47:55 +02:00
abcdefg30
0bca700bff Let the utility fix cosmetics in the chrome yamls 2015-11-29 20:47:46 +02:00
abcdefg30
0d286e12b2 Fix potential crashes in the upgrade rules 2015-11-29 20:47:38 +02:00
Oliver Brakmann
811acc5a58 Merge pull request #10144 from cjshmyr/missile-prepfix
Set missile default acceleration to 0 (prep only)
2015-11-29 13:10:05 +01:00
Curtis Shmyr
265faa3452 Set missile default acceleration to 0 2015-11-28 21:59:05 -07:00
Paul Chote
bc51d6638d Add a server log line for player joins. 2015-11-28 15:45:30 +01:00
Oliver Brakmann
60a27f9f83 Merge pull request #10130 from pchote/revert-bridge-fix
Revert "add custom selection sizes to aid the renderer" on prep-1512
2015-11-28 15:25:25 +01:00
Paul Chote
a7cfaff96c Revert "add custom selection sizes to aid the renderer"
This reverts commit c3dce785a5.
2015-11-28 09:58:44 +00:00
Paul Chote
d957a5a15e Implement suicide saboteur. 2015-11-27 14:31:20 +01:00
Paul Chote
026a27296c Fix C4Demolition trait doc punctuation. 2015-11-27 14:31:13 +01:00
Paul Chote
f7c2043e06 Add enter behaviour customisation to C4Demolition. 2015-11-27 14:31:02 +01:00
OmegaBolt
4efe8d091c D2k Deviator balance 2015-11-25 22:54:38 +02:00
Matthias Mailänder
97e810d753 simplify the hierarchy section hierarchy 2015-11-25 22:35:22 +02:00
Matthias Mailänder
7ed82fa4c7 make the "Game" component read-only 2015-11-25 22:35:15 +02:00
reaperrr
32b4402ba0 Give Missile Tank weapon a range limit and reduce Quad rocket range limit 2015-11-25 22:30:36 +02:00
DArcy Rush
897c0f01f0 Add second exit to kennel 2015-11-25 22:16:55 +02:00
Paul Chote
b21b9c9762 Fix the ConvertSpriteToPngCommand. 2015-11-25 22:08:30 +02:00
Paul Chote
5bcc83bc0c Add upgraded tag to D2K production structures. 2015-11-25 22:02:28 +02:00
Paul Chote
509662d8a6 Add SelectionDecoration flag to WithDecoration. 2015-11-25 22:02:14 +02:00
Paul Chote
fd171d842e Add new primary building tag artwork. 2015-11-25 22:02:07 +02:00
Paul Chote
9258face3c Fix D2K outpost artwork offsets and selection box. 2015-11-25 22:01:41 +02:00
Oliver Brakmann
41e5032d48 Fix worms not attacking anything anymore 2015-11-22 21:56:49 +01:00
OmegaBolt
6006dbd235 D2k, fix #10037 terrain regressions 2015-11-22 21:56:35 +01:00
OmegaBolt
b7e7ff7fc0 D2k spice no longer erases map details & terrain fixes 2015-11-22 18:22:11 +01:00
reaperrr
6d98635e94 Add invisible spice bloom spawn
When real spiceblooms explode, they spawn an invisible
spicebloom.spawnpoint actor, which only 'grows' when the cell is of
GrowthTerrainType. When 'exploding', it then displays the animation of
blowing spice up into the air, and spawns a real spicebloom.

This replicates the original behavior where spice blooms would only grow
on sand cells that had been harvested and free of spice.
2015-11-22 17:24:03 +01:00
reaperrr
46762140a5 Improve spice bloom seeding mechanism
Improve seeding target cell determination.
Draw out spice seedable cells before loop.
Default to null for SpiceBloom.Weapon.
Pull spice seeding into its own method.
Skip it completely if no seed weapon is defined.
2015-11-22 17:19:28 +01:00
reaperrr
09cdf3b257 Only allow spice blooms to grow on certain terrain types 2015-11-22 17:19:28 +01:00
RoosterDragon
033e469bea Prevent invalid cells checks in SeedsResource. 2015-11-22 08:05:10 +01:00
Paul Chote
7e680b9c02 Fix width of the MP filter checkboxes. 2015-11-22 01:36:32 +00:00
abcdefg30
a56d367f60 Add an upgrade rule for the Upgrades -> DeployedUpgrades change 2015-11-22 01:06:16 +01:00
Pavel Penev
bd2629196a Update thumpers and Mobile Sensor Arrays to the new DeployToUpgrade
Currently those are the only two usages of the trait.
2015-11-22 01:01:56 +01:00
Pavel Penev
011f566c6f Add support for deploy animations using a specific upgrade
Also ignore orders while playing a deploy/undeploy animation. Fixes 9242.

We grant a level 1 Undeployed upgrade(s) when the actor is created which enables the default behavior of the actor. When the actor receives a deploy order, the Undeployed upgrade(s) is(are) revoked, which may or may not be used to enable a trait to play a deploy animation. Once the deployment is complete, the Deployed upgrade(s) is(are) granted.
Then do the reverse when undeploying, disabling weapons and other systems first by revoking the Deployed upgrade(s), and granting the Undeployed upgrade(s) when the undeploy animation is done.
2015-11-22 01:01:40 +01:00
abcdefg30
161907b852 Fix 'oldlst's letting units enter it (after delivering them) 2015-11-21 23:23:36 +01:00
abcdefg30
1576101a2c Add IsDead checks to gdi06 2015-11-21 23:15:43 +01:00
abcdefg30
67ba3e53f2 Fix the harvester stopping when you reach the base in gdi05 2015-11-21 23:07:16 +01:00
DArcy Rush
52b061a8a8 Hide ammobox healthbar. 2015-11-21 22:49:06 +01:00
abcdefg30
5755f4048d Merge pull request #10057 from atlimit8/FinishRevertingMultilineSupportForSimpleTooltipLogic
Finish reverting multiline support for SimpleTooltipLogic
2015-11-21 22:37:36 +01:00
atlimit8
b1b210ea6d Finish reverting #9925 & #9715 2015-11-21 14:28:33 -06:00
abcdefg30
fc8f1d40c5 Fix the rushAI building spens/syrds without being able to build naval units 2015-11-21 21:02:22 +01:00
Paul Chote
5d83a5a7d6 Restore earlier spy targeting behaviour. 2015-11-21 20:58:17 +01:00
Paul Chote
8c24e87de2 Make CanTarget modifiers a ref parameter. 2015-11-21 20:58:17 +01:00
reaperrr
13c4f16272 Increase the default range of angles that missiles can launch at 2015-11-21 21:12:23 +02:00
Pavel Penev
20a33b8411 Make GPS respect shroud 2015-11-21 18:59:41 +01:00
Pavel Penev
51fcb55b22 Remove byPlayer parameter from IFogVisibilityModifier.HasFogVisibility() 2015-11-21 18:59:41 +01:00
Pavel Penev
8e7469e194 Add IFogVisibilityModifier.IsVisible(Actor) 2015-11-21 18:59:41 +01:00
Pavel Penev
7e0617075f Track GpsDot state per player
Should let us fix the issue mentioned in https://github.com/OpenRA/OpenRA/pull/10007#issuecomment-157409237
2015-11-21 18:59:41 +01:00
Pavel Penev
4ff6ba8c37 Don't attack actors that you can't see 2015-11-21 18:59:35 +01:00
atlimit8
958a72aaf8 Revert Multi-line tooltips due to lack of multi-line support.
This reverts #9925 & #9715.
2015-11-21 11:08:11 -06:00
Oliver Brakmann
e7df117892 Merge pull request #10019 from r34ch/ra-map-pool
Add a couple community maps to the pool.
2015-11-21 14:55:31 +01:00
reaperrr
57d49d0aeb Fix D2k wormspawner editor art 2015-11-19 19:36:54 +01:00
reaperrr
ee86b5f45f Fix D2k effect palette shadow index 2015-11-19 19:36:54 +01:00
reaperrr
ba0e3130af Fix mpspawn, waypoint and camera art
All mpspawn, waypoint and camera editor art uses the correct shadow and remap colors.
Additionally added camera art to CnC, D2k and TS.
2015-11-19 19:36:54 +01:00
Pavel Penev
64427dd1a6 Fix UpgradeActorsNear revoking upgrades after the actor dies
Very hard bug to reproduce: if you can power down a stealth generator just as it is dieing you can hit a race condition that leads to ActorExited() being called to revoke the upgrades after they have already been revoked once.
2015-11-19 19:00:12 +01:00
abcdefg30
7412669d03 Add production acceleration to d2k
except for starports
2015-11-17 19:33:26 +01:00
Curtis Shmyr
ec8e9f537d Increase TRUK and DTRK vision from 3 to 4 cells 2015-11-17 19:25:39 +01:00
DArcy Rush
9c71d555c1 Add a couple community maps to the pool. 2015-11-17 08:39:43 +00:00
abcdefg30
0f191484d7 Fix a crash when using the chronoshift power
if the "chronosphere-actor" doesn't have an "active" sequence
2015-11-16 22:00:30 +00:00
Pavel Penev
4be5979b90 Add a null check to CachedTransform.Update() 2015-11-16 21:52:19 +00:00
Pavel Penev
6bd092f192 Check if WithSpriteBody is available in GrantUpgradePower 2015-11-16 13:58:24 +01:00
Oliver Brakmann
bd6bcda6d0 Merge pull request #9993 from pchote/nsprocess-fix
Switch to a (hopefully) working NSProcess url.
2015-11-14 22:52:56 +01:00
Paul Chote
f12deca9ca Switch to a (hopefully) working NSProcess url. 2015-11-14 21:47:21 +00:00
Oliver Brakmann
5ea82b4e5e Increase range of flak truck against air targets
From 6 cells to 8 cells
2015-11-14 22:10:40 +01:00
DArcy Rush
2d210a97df Rebalance Tesla Tank.
Allow attacking of target regardless of facing.
2015-11-14 22:10:40 +01:00
DArcy Rush
f03a100115 Rebalance phase transport.
Passengers increased to 4, speed and LoS increased.
2015-11-14 22:10:40 +01:00
DArcy Rush
1e30684782 Reduce chronotank charge time.
Chronotank charge decreased from 20s to 12s.
2015-11-14 22:10:40 +01:00
Curtis Shmyr
abb45197b1 lower InfiltrateForPowerOutage time from 30->20sec 2015-11-14 19:34:37 +00:00
Paul Chote
a7935f6166 Check deploy location at the time of transform. 2015-11-14 16:46:18 +01:00
Oliver Brakmann
7b75a9c2f7 Fix reinforcements appearing in soviet04a/b despite killed radar dome 2015-11-14 15:01:51 +02:00
reaperrr
9b694f6044 Fix spice bloom editor visibility 2015-11-14 12:09:07 +00:00
Biofreak1987
0a75019aa0 Fix ai building in soviet04/06 missions despite killed construction yard 2015-11-13 20:53:26 +01:00
Biofreak1987
d3e41b6d50 Fix nod05 airstrike Lua crash and update map rules
- Fix airstrike Lua crash when there are no targets
- Increased harvester search range
- Hide owner row from husks and walls

Changes to make it closer to the original:
- Added e3 and sandbag production
- Made airstrikes gdi only
- Limited airstrike squadsize to 1
- Allow to attack civilian buildings
2015-11-12 22:52:38 +01:00
Oliver Brakmann
18f3e1f57b Merge pull request #9971 from pchote/tooltip-fix
Fix map editor crash
2015-11-12 19:32:02 +01:00
Paul Chote
1760f8acd6 Fix tooltip colour definition. 2015-11-11 23:15:33 +00:00
Pavel Penev
4154553318 Validate player color when changing slots in the lobby 2015-11-11 22:59:48 +00:00
Oliver Brakmann
e5e8e64ace Fix bogus owner check in Lua API production code 2015-11-11 22:24:05 +00:00
OmegaBolt
0ae58c10ad D2k thumper worm attraction balance 2015-11-11 23:01:53 +02:00
Paul Chote
89ea810cbe Truncate player name in replay browser. 2015-11-11 22:40:00 +02:00
Paul Chote
f6cf35c99c Truncate player name in observer stats and diplomacy panels. 2015-11-11 22:39:52 +02:00
Paul Chote
5fbaeabb6d Truncate player name in ingame menu stats. 2015-11-11 22:39:37 +02:00
Paul Chote
581907f53a Prevent frozen actors leaking through shroud. 2015-11-11 22:33:16 +02:00
Paul Chote
7cf4be9d78 Add copy/paste to the map editor. 2015-11-11 01:02:16 +02:00
Paul Chote
bdf9f5e201 Make editor brushes disposable. 2015-11-11 01:02:09 +02:00
Pavel Penev
b26b1f410f Enable AutoTarget to aquire targets under fog if GPS is present 2015-11-10 22:27:40 +01:00
OmegaBolt
ce69abe1f2 D2k fixed armour & AI typos 2015-11-10 22:04:09 +01:00
atlimit8
b3d2b36875 Make ActorSelector tooltips better match actor tooltips in map editor 2015-11-10 21:08:53 +01:00
atlimit8
f28b37323f Made editor actor preview tooltip easier to read 2015-11-10 21:08:53 +01:00
atlimit8
7c82ab60e8 Multiline support for SimpleTooltipLogic 2015-11-10 21:08:53 +01:00
Zimmermann Gyula
d14fbd0f84 Fix observer GUI showing disabled support powers. 2015-11-09 21:07:19 +01:00
OmegaBolt
c567f5cbdd D2k super weapon timers 2015-11-09 21:51:02 +02:00
OmegaBolt
51a9831fc2 D2k Ornithopter bomb damage increase 2015-11-09 19:58:36 +01:00
OmegaBolt
4d5777cbc9 D2k, fixed infantry moving slow on dunes and rough 2015-11-08 21:59:07 +02:00
Paul Chote
a6a835d95f Truncate long player names in lobby. 2015-11-08 17:24:24 +02:00
Paul Chote
e16be14ec7 Truncate long server names in MP browser. 2015-11-08 17:24:19 +02:00
Paul Chote
11939d3af1 Truncate long map name in replay browser. 2015-11-08 17:24:13 +02:00
Paul Chote
feb133c2eb Truncate long map name / mod version in MP panel. 2015-11-08 17:24:02 +02:00
Paul Chote
f6361b031b Truncate long map name / author in map chooser. 2015-11-08 17:23:56 +02:00
Paul Chote
36967951e6 Truncate long map name / author in lobby. 2015-11-08 17:23:39 +02:00
Paul Chote
012a9d71ba Add helpers for truncating long labels. 2015-11-08 17:23:31 +02:00
Paul Chote
cff369850c Set the default map location to the support dir. 2015-11-08 14:49:38 +02:00
DArcy Rush
136b4c0fa9 Add self to AUTHORS. 2015-11-08 11:40:37 +00:00
DArcy Rush
aae2459d0b Move blossom trees south one cell in cnc64gdi01. 2015-11-08 11:40:27 +00:00
teees
d47ba1d135 only change editor brushes on mouseup
let other uses of the right mouse button bubble up
2015-11-08 11:30:25 +00:00
teees
133cffff24 Handle mouse up event in default editor brush 2015-11-08 11:30:08 +00:00
Oliver Brakmann
52f6c993d5 Fix lint warning in gdi06 2015-11-08 11:17:22 +00:00
Biofreak1987
21eecb048d Fix soviet06 prerequisites 2015-11-08 09:48:47 +01:00
Oliver Brakmann
b97398a671 Fix typo in WeatherOverlayInfo 2015-11-08 09:14:22 +01:00
Curtis Shmyr
a285c908db Added 3 Lua player API properties 2015-11-08 08:53:15 +01:00
reaperrr
35b39162ff Fix spice debris target cell randomization 2015-11-07 18:32:21 +01:00
reaperrr
0b5b6adc7c Improve SpiceExplosion and add BloomExplosion
Fixes angle and loudness of spice debris, adds BloomExplosion to add proper animation and damage crushing actors.
2015-11-07 18:32:14 +01:00
reaperrr
5357eb6f29 Increase variation and average of spice bloom growth 2015-11-07 18:32:04 +01:00
reaperrr
e274ac5ff8 Make sure that a spice bloom spawns at least the minimum of pieces 2015-11-07 18:31:59 +01:00
reaperrr
f8e39bd142 Reduce spawned spice per piece 2015-11-07 18:31:56 +01:00
Pavel Penev
48881f4e73 Fix repair pad reference in units' Repairable trait 2015-11-07 18:31:52 +01:00
Pavel Penev
d2eb56183d Buff D2k repair pads 2015-11-07 18:10:40 +01:00
reaperrr
afd75c7f55 Fix WithSpriteRotorOverlay upgrade rule to account for @ separator 2015-11-07 16:18:29 +02:00
reaperrr
6d1551c9fd Change engineVersion for upgrade rules of changes made between 20150808 and 20150919
Those changes were merged after the feature-freeze, but predate the stable release. Changing the engine version to the day after release makes it easier for modders to upgrade without using bogus engine dates.
2015-11-07 16:18:18 +02:00
Paul Chote
a015b0990b Fetch battlefield news once per game launch. 2015-11-07 14:13:31 +02:00
Paul Chote
6dd08bbc98 Fix global chat line margins. 2015-11-07 12:43:14 +01:00
Paul Chote
343616e924 Add unread message count to lobby chat tabs. 2015-11-07 12:43:03 +01:00
Paul Chote
91242a832f Add Tick method to ChromeLogic. 2015-11-07 12:43:03 +01:00
OmegaBolt
1c53bfed11 D2k crate balance
- Fixed prerequisites.
- Added more infantry crates.
- Upped the create explosion damage, though it is still very little. Takes about a third health off a Trike.
2015-11-06 19:47:34 +01:00
RoosterDragon
e5e7d037a3 Remove event handler when disposed in MainMenuLogic.
Removes a static event handler to Game.OnRemoteDirectConnect which allow the GC to reclaim the MainMenuLogic class after it has been disposed.
2015-11-05 22:49:25 +01:00
RoosterDragon
2a301392c6 Remove event handlers to CellEntryChanged when done.
Several classes would attach event handlers to the Map which would live longer then they did. Detaching them when no longer needed allows the GC to reclaim them.
2015-11-05 22:49:25 +01:00
RoosterDragon
e35bb8ea95 Detach event handlers on dispose in TerrainSpriteLayer.
The WorldRenderer outlives the TerrainSpriteLayer and thus keeps it alive longer than expected via the event handler. We detach it to allow the GC to reclaim it.
2015-11-05 22:49:25 +01:00
Pavel Penev
7a4002ff08 Fix the C17 delivery plane doing a steep dive when trying to land 2015-11-05 19:49:04 +00:00
RoosterDragon
523fe20255 Work around a hang on shutdown caused by IRC.
Our IRC client doesn't shut down properly - but we only need to shut it down when we're about to close the game anyway, so we just don't bother since it won't hurt anybody.
2015-11-05 20:42:15 +01:00
Zimmermann Gyula
31e0c8b2e3 Fix (HealthPercentage)DamageWarheads ignoring stances/TargetTypes.
Moves the IsValidAgainst check from SpreadDamage level to Damage level
and adds a duplication to HealthPercentageDamageWarhead.
2015-11-04 22:34:48 +00:00
Pavel Penev
7c37d1cfcc Fix server filter width 2015-11-04 22:31:27 +01:00
Oliver Brakmann
8e76a109bd Fix viceroids being spawned by crushed infantry 2015-11-03 10:33:40 +00:00
Michael Rätzel
ba9b18ccb7 reset RallyPoint Location after capture. resolves #9163. 2015-10-31 21:59:03 +01:00
1680 changed files with 42987 additions and 57410 deletions

9
.gitignore vendored
View File

@@ -70,11 +70,4 @@ StyleCopViolations.xml
# SublimeText
*.sublime-project
*.sublime-workspace
# NUnit
/TestResult.xml
/lib/
# Support directory
/Support
*.sublime-workspace

View File

@@ -1,9 +0,0 @@
{
"name": "OpenRA"
, "files": [ { "git": 1 } ]
, "build": {
"directory": "."
, "build": "make all"
, "clean": "make clean"
}
}

View File

@@ -2,7 +2,7 @@
# see travis-ci.org for details
language: csharp
mono: 4.2.1
mono: 4.0.0
# http://docs.travis-ci.com/user/migrating-from-legacy
sudo: false
@@ -28,14 +28,12 @@ env:
# Run the build script
# Check source code with StyleCop
# call OpenRA to check for YAML errors
# Run the NUnit tests
script:
- travis_retry make all-dependencies
- make all SDK="-sdk:4.0"
- make all
- make check
- make check-scripts
- make test
- make nunit
# Automatically update the trait documentation and Lua API
after_success:

17
AUTHORS
View File

@@ -55,7 +55,6 @@ Also thanks to:
* Eric Bajumpaa (SteelPhase)
* Evgeniy Sergeev (evgeniysergeev)
* Fahrradkette
* Florian Wiesbauer (FiveAces)
* Frank Razenberg (zzattack)
* Gareth Needham (Ripley`)
* Glen Anderson (GlenAnderson)
@@ -76,12 +75,9 @@ Also thanks to:
* Jeff Harris (jeff_1amstudios)
* Jes
* Joakim Lindberg (booom3)
* John Turner (whinis)
* Jonas A. Lind (SoScared)
* Joppy Furr
* Kanar
* Kenny Hoxworth (hoxworth)
* Kevin Azzam (ChaoticMind)
* Krishnakanth Mallik
* Kyrre Soerensen (zypres)
* Lawrence Wang
@@ -94,7 +90,6 @@ Also thanks to:
* Matthijs Benschop (Nerdie)
* Max621
* Max Ugrumov (katzsmile)
* Michael Rätzel
* Michael Sztolcman (s1w_)
* Mustafa Alperen Seki (MustaphaTR)
* Neil Shivkar (havok13888)
@@ -106,16 +101,12 @@ Also thanks to:
* Paul Dovydaitis (pdovy)
* Pavlos Touboulidis (pav)
* Pizzaoverhead
* Piët Delport (pjdelport)
* Psydev
* Raphael Vogt (TheRaffy, Yellow)
* Raymond Bedrossian (Squiggles211)
* Raymond Martineau (mart0258)
* Riderr3
* riiga
* Rikhardur Bjarni Einarsson (WolfGaming)
* Sascha Biedermann (bidifx)
* Sean Hunt (coppro)
* Sebastien Kerguen (xanax)
* Shawn Collins (UberWaffe)
* Simon Verbeke (Saticmotion)
@@ -148,8 +139,11 @@ distributed under the CC BY-SA 3.0 license.
Using SharpFont created by Robert Rouhani and
distributed under the MIT license.
Using SDL2-CS and OpenAL-CS created by Ethan
Lee and released under the zlib license.
Using the Open Toolkit distributed under the
MIT license.
Using SDL2# created by Ethan Lee and released
under the zlib license.
Using Eluant created by Chris Howie and released
under the MIT license.
@@ -170,3 +164,4 @@ distributed under the LGPL version 2.1 or later.
Finally, special thanks goes to the original teams
at Westwood Studios and EA for creating the classic
games which OpenRA aims to reimagine.

View File

@@ -34,8 +34,10 @@ Type `sudo make install-all` for system wide installation. Run `make install-lin
Debian/Ubuntu
-------------
* mono-dmcs
* libmono-system-windows-forms4.0-cil
* nuget
* mono-devel
* cli-common-dev (>= 2.10)
* libfreetype6
* libopenal1
* liblua5.1-0

136
Makefile
View File

@@ -6,11 +6,6 @@
# to compile with development tools, run:
# make all [DEBUG=false]
#
# to check unit tests (requires NUnit version >= 2.6), run:
# make nunit [NUNIT_CONSOLE=<path-to/nunit[2]-console>] [NUNIT_LIBS_PATH=<path-to-libs-dir>] [NUNIT_LIBS=<nunit-libs>]
# Use NUNIT_CONSOLE if nunit[3|2]-console was not downloaded by `make dependencies` nor is it in bin search paths
# Use NUNIT_LIBS_PATH if NUnit libs are not in search paths. Include trailing /
# Use NUNIT_LIBS if NUnit libs have different names (such as including a prefix or suffix)
# to check the official mods for erroneous yaml files, run:
# make test
#
@@ -42,13 +37,10 @@
############################## TOOLCHAIN ###############################
#
SDK ?=
CSC = mcs $(SDK)
CSC = dmcs
CSFLAGS = -nologo -warn:4 -codepage:utf8 -unsafe -warnaserror
DEFINE = TRACE
COMMON_LIBS = System.dll System.Core.dll System.Data.dll System.Data.DataSetExtensions.dll System.Drawing.dll System.Xml.dll thirdparty/download/ICSharpCode.SharpZipLib.dll thirdparty/download/FuzzyLogicLibrary.dll thirdparty/download/Mono.Nat.dll thirdparty/download/MaxMind.Db.dll thirdparty/download/MaxMind.GeoIP2.dll thirdparty/download/Eluant.dll thirdparty/download/SmarIrc4net.dll
NUNIT_LIBS_PATH :=
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
DEBUG = true
ifeq ($(DEBUG), $(filter $(DEBUG),false no n off 0))
@@ -87,7 +79,7 @@ INSTALL_PROGRAM = $(INSTALL) -m755
INSTALL_DATA = $(INSTALL) -m644
# program targets
CORE = pdefault game utility server
CORE = pdefault pnull game utility
TOOLS = gamemonitor
VERSION = $(shell git name-rev --name-only --tags --no-undefined HEAD 2>/dev/null || echo git-`git rev-parse --short HEAD`)
@@ -118,9 +110,15 @@ pdefault_SRCS := $(shell find OpenRA.Platforms.Default/ -iname '*.cs')
pdefault_TARGET = OpenRA.Platforms.Default.dll
pdefault_KIND = library
pdefault_DEPS = $(game_TARGET)
pdefault_LIBS = $(COMMON_LIBS) thirdparty/download/SDL2-CS.dll thirdparty/download/OpenAL-CS.dll $(pdefault_DEPS)
PROGRAMS += pdefault
platforms: $(pdefault_TARGET)
pdefault_LIBS = $(COMMON_LIBS) thirdparty/download/SDL2-CS.dll $(pdefault_DEPS)
pnull_SRCS := $(shell find OpenRA.Platforms.Null/ -iname '*.cs')
pnull_TARGET = OpenRA.Platforms.Null.dll
pnull_KIND = library
pnull_DEPS = $(game_TARGET)
pnull_LIBS = $(COMMON_LIBS) $(pnull_DEPS)
PROGRAMS += pdefault pnull
platforms: $(pdefault_TARGET) $(pnull_TARGET)
# Mods Common
mod_common_SRCS := $(shell find OpenRA.Mods.Common/ -iname '*.cs')
@@ -131,16 +129,6 @@ mod_common_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) thirdparty/download/StyleCop.dl
PROGRAMS += mod_common
mod_common: $(mod_common_TARGET)
# NUnit testing
test_dll_SRCS := $(shell find OpenRA.Test/ -iname '*.cs')
test_dll_TARGET = OpenRA.Test.dll
test_dll_KIND = library
test_dll_DEPS = $(game_TARGET) $(mod_common_TARGET)
test_dll_FLAGS = -warn:1
test_dll_LIBS = $(COMMON_LIBS) $(game_TARGET) $(mod_common_TARGET) $(NUNIT_LIBS)
PROGRAMS += test_dll
test_dll: $(test_dll_TARGET)
##### Official Mods #####
STD_MOD_LIBS = $(game_TARGET)
@@ -196,6 +184,9 @@ check: utility mods
@echo "Checking for code style violations in OpenRA.Platforms.Default..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Platforms.Default
@echo
@echo "Checking for code style violations in OpenRA.Platforms.Null..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Platforms.Null
@echo
@echo "Checking for code style violations in OpenRA.GameMonitor..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.GameMonitor
@echo
@@ -219,31 +210,6 @@ check: utility mods
@echo
@echo "Checking for code style violations in OpenRA.Test..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Test
@echo
@echo "Checking for explicit interface violations..."
@mono --debug OpenRA.Utility.exe all --check-explicit-interfaces
@echo
@echo "Checking for code style violations in OpenRA.Server..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Server
NUNIT_CONSOLE := $(shell test -f thirdparty/download/nunit3-console.exe && echo mono thirdparty/download/nunit3-console.exe || \
which nunit3-console 2>/dev/null || which nunit2-console 2>/dev/null || which nunit-console 2>/dev/null)
nunit: test_dll
@echo
@echo "Checking unit tests..."
@if [ "$(NUNIT_CONSOLE)" = "" ] ; then \
echo 'nunit[3|2]-console not found!'; \
echo 'Was "make dependencies" called or is NUnit installed?'>&2; \
echo 'See "make help".'; \
exit 1; \
fi
@if $(NUNIT_CONSOLE) --help | head -n 1 | grep -E "NUnit version (1|2\.[0-5])";then \
echo 'NUnit version >= 2.6 required'>&2; \
echo 'Try "make dependencies" first to use NUnit from NuGet.'>&2; \
echo 'See "make help".'; \
exit 1; \
fi
@$(NUNIT_CONSOLE) --noresult OpenRA.Test.nunit
test: utility mods
@echo
@@ -280,15 +246,6 @@ utility_LIBS = $(COMMON_LIBS) $(utility_DEPS) thirdparty/download/ICSharpCode.Sh
PROGRAMS += utility
utility: $(utility_TARGET)
# Dedicated server
server_SRCS := $(shell find OpenRA.Server/ -iname '*.cs')
server_TARGET = OpenRA.Server.exe
server_KIND = exe
server_DEPS = $(game_TARGET)
server_LIBS = $(COMMON_LIBS) $(server_DEPS)
PROGRAMS += server
server: $(server_TARGET)
# Patches binary headers to work around a mono bug
fixheader.exe: packaging/fixheader.cs
@echo CSC fixheader.exe
@@ -318,7 +275,7 @@ $(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog))))
#
default: core
core: game platforms mods utility server
core: game platforms mods utility
tools: gamemonitor
@@ -339,6 +296,7 @@ cli-dependencies:
@./thirdparty/fetch-thirdparty-deps.sh
@ $(CP_R) thirdparty/download/*.dll .
@ $(CP_R) thirdparty/download/*.dll.config .
@ $(CP) thirdparty/SDL2-CS.dll.config .
linux-dependencies: cli-dependencies linux-native-dependencies
@@ -362,9 +320,7 @@ all-dependencies: cli-dependencies windows-dependencies osx-dependencies
version: mods/ra/mod.yaml mods/cnc/mod.yaml mods/d2k/mod.yaml mods/ts/mod.yaml mods/modchooser/mod.yaml mods/all/mod.yaml
@for i in $? ; do \
awk '{sub("Version:.*$$","Version: $(VERSION)"); print $0}' $${i} > $${i}.tmp && \
awk '{sub("\tmodchooser:.*$$","\tmodchooser: $(VERSION)"); print $0}' $${i}.tmp > $${i}.tmp2 && \
awk '{sub("/[^/]*: User$$", "/$(VERSION): User"); print $0}' $${i}.tmp2 > $${i} && \
rm $${i}.tmp $${i}.tmp2; \
mv -f $${i}.tmp $${i} ; \
done
docs: utility mods version
@@ -403,7 +359,6 @@ install-core: default
@$(CP_R) glsl "$(DATA_INSTALL_DIR)"
@$(CP_R) lua "$(DATA_INSTALL_DIR)"
@$(CP) SDL2-CS* "$(DATA_INSTALL_DIR)"
@$(CP) OpenAL-CS* "$(DATA_INSTALL_DIR)"
@$(CP) Eluant* "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) ICSharpCode.SharpZipLib.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) FuzzyLogicLibrary.dll "$(DATA_INSTALL_DIR)"
@@ -468,22 +423,9 @@ endif
@$(INSTALL_PROGRAM) -m +rx openra "$(BIN_INSTALL_DIR)"
@-$(RM) openra
@echo "#!/bin/sh" > openra-server
@echo 'cd "$(gameinstalldir)"' >> openra-server
ifeq ($(DEBUG), $(filter $(DEBUG),false no n off 0))
@echo 'mono OpenRA.Server.exe "$$@"' >> openra-server
else
@echo 'mono --debug OpenRA.Server.exe "$$@"' >> openra-server
endif
@$(INSTALL_DIR) "$(BIN_INSTALL_DIR)"
@$(INSTALL_PROGRAM) -m +rx openra-server "$(BIN_INSTALL_DIR)"
@-$(RM) openra-server
uninstall:
@-$(RM_R) "$(DATA_INSTALL_DIR)"
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra"
@-$(RM_F) "$(BIN_INSTALL_DIR)/openra-server"
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra.desktop"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/openra.png"
@@ -495,38 +437,32 @@ uninstall:
@-$(RM_F) "$(DESTDIR)$(mandir)/man6/openra.6"
help:
@echo 'to compile, run:'
@echo ' make [DEBUG=false]'
@echo to compile, run:
@echo \ \ make [DEBUG=false]
@echo
@echo 'to compile with development tools, run:'
@echo ' make all [DEBUG=false]'
@echo to compile with development tools, run:
@echo \ \ make all [DEBUG=false]
@echo
@echo 'to check unit tests (requires NUnit version >= 2.6), run:'
@echo ' make nunit [NUNIT_CONSOLE=<path-to/nunit[3|2]-console>] [NUNIT_LIBS_PATH=<path-to-libs-dir>] [NUNIT_LIBS=<nunit-libs>]'
@echo ' Use NUNIT_CONSOLE if nunit[3|2]-console was not downloaded by `make dependencies` nor is it in bin search paths'
@echo ' Use NUNIT_LIBS_PATH if NUnit libs are not in search paths. Include trailing /'
@echo ' Use NUNIT_LIBS if NUnit libs have different names (such as including a prefix or suffix)'
@echo to check the official mods for erroneous yaml files, run:
@echo \ \ make test
@echo
@echo 'to check the official mods for erroneous yaml files, run:'
@echo ' make test'
@echo to generate documentation aimed at modders, run:
@echo \ \ make docs
@echo
@echo 'to generate documentation aimed at modders, run:'
@echo ' make docs'
@echo to install, run:
@echo \ \ make \[prefix=/foo\] \[bindir=/bar/bin\] install
@echo
@echo 'to install, run:'
@echo ' make [prefix=/foo] [bindir=/bar/bin] install'
@echo to install with development tools, run:
@echo \ \ make \[prefix=/foo\] \[bindir=/bar/bin\] install-all
@echo
@echo 'to install with development tools, run:'
@echo ' make [prefix=/foo] [bindir=/bar/bin] install-all'
@echo to install Linux startup scripts, desktop files and icons
@echo \ \ make install-linux-shortcuts [DEBUG=false]
@echo
@echo 'to install Linux startup scripts, desktop files and icons'
@echo ' make install-linux-shortcuts [DEBUG=false]'
@echo to uninstall, run:
@echo \ \ make uninstall
@echo
@echo 'to uninstall, run:'
@echo ' make uninstall'
@echo
@echo 'to start the game, run:'
@echo ' openra'
@echo to start the game, run:
@echo \ \ openra
@@ -537,4 +473,4 @@ help:
.SUFFIXES:
.PHONY: core tools package all mods clean distclean dependencies version $(PROGRAMS) nunit
.PHONY: core tools package all mods clean distclean dependencies version $(PROGRAMS)

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -25,13 +24,6 @@ namespace OpenRA
{
public sealed class Actor : IScriptBindable, IScriptNotifyBind, ILuaTableBinding, ILuaEqualityBinding, ILuaToStringBinding, IEquatable<Actor>, IDisposable
{
internal struct SyncHash
{
public readonly ISync Trait;
public readonly int Hash;
public SyncHash(ISync trait, int hash) { Trait = trait; Hash = hash; }
}
public readonly ActorInfo Info;
public readonly World World;
@@ -52,7 +44,6 @@ namespace OpenRA
public Rectangle VisualBounds { get; private set; }
public IEffectiveOwner EffectiveOwner { get; private set; }
public IOccupySpace OccupiesSpace { get; private set; }
public ITargetable[] Targetables { get; private set; }
public bool IsIdle { get { return currentActivity == null; } }
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
@@ -70,8 +61,6 @@ namespace OpenRA
}
}
internal IEnumerable<SyncHash> SyncHashes { get; private set; }
readonly IFacing facing;
readonly IHealth health;
readonly IRenderModifier[] renderModifiers;
@@ -108,9 +97,6 @@ namespace OpenRA
}
}
// PERF: Cache all these traits as soon as the actor is created. This is a fairly cheap one-off cost per
// actor that allows us to provide some fast implementations of commonly used methods that are relied on by
// performance-sensitive parts of the core game engine, such as pathfinding, visibility and rendering.
Bounds = DetermineBounds();
VisualBounds = DetermineVisualBounds();
EffectiveOwner = TraitOrDefault<IEffectiveOwner>();
@@ -121,13 +107,6 @@ namespace OpenRA
disables = TraitsImplementing<IDisable>().ToArray();
visibilityModifiers = TraitsImplementing<IVisibilityModifier>().ToArray();
defaultVisibility = Trait<IDefaultVisibility>();
Targetables = TraitsImplementing<ITargetable>().ToArray();
SyncHashes =
TraitsImplementing<ISync>()
.Select(sync => Pair.New(sync, Sync.GetHashFunction(sync)))
.ToArray()
.Select(pair => new SyncHash(pair.First, pair.Second(pair.First)));
}
Rectangle DetermineBounds()
@@ -161,7 +140,7 @@ namespace OpenRA
public void Tick()
{
var wasIdle = IsIdle;
currentActivity = ActivityUtils.RunActivity(this, currentActivity);
currentActivity = Traits.Util.RunActivity(this, currentActivity);
if (!wasIdle && IsIdle)
foreach (var n in TraitsImplementing<INotifyBecomingIdle>())
@@ -170,7 +149,6 @@ namespace OpenRA
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
// PERF: Avoid LINQ.
var renderables = Renderables(wr);
foreach (var modifier in renderModifiers)
renderables = modifier.ModifyRender(this, wr, renderables);
@@ -179,13 +157,6 @@ namespace OpenRA
IEnumerable<IRenderable> Renderables(WorldRenderer wr)
{
// PERF: Avoid LINQ.
// Implementations of Render are permitted to return both an eagerly materialized collection or a lazily
// generated sequence.
// For large amounts of renderables, a lazily generated sequence (e.g. as returned by LINQ, or by using
// `yield`) will avoid the need to allocate a large collection.
// For small amounts of renderables, allocating a small collection can often be faster and require less
// memory than creating the objects needed to represent a sequence.
foreach (var render in renders)
foreach (var renderable in render.Render(this, wr))
yield return renderable;
@@ -235,7 +206,6 @@ namespace OpenRA
public override string ToString()
{
// PERF: Avoid format strings.
var name = Info.Name + " " + ActorID;
if (!IsInWorld)
name += " (not in world)";
@@ -304,7 +274,7 @@ namespace OpenRA
if (wasInWorld)
w.Add(this);
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
foreach (var t in this.TraitsImplementing<INotifyOwnerChanged>())
t.OnOwnerChanged(this, oldOwner, newOwner);
});
}
@@ -335,7 +305,6 @@ namespace OpenRA
public bool IsDisabled()
{
// PERF: Avoid LINQ.
foreach (var disable in disables)
if (disable.Disabled)
return true;
@@ -344,7 +313,6 @@ namespace OpenRA
public bool CanBeViewedByPlayer(Player player)
{
// PERF: Avoid LINQ.
foreach (var visibilityModifier in visibilityModifiers)
if (!visibilityModifier.IsVisible(this, player))
return false;
@@ -352,33 +320,6 @@ namespace OpenRA
return defaultVisibility.IsVisible(this, player);
}
public IEnumerable<string> GetAllTargetTypes()
{
// PERF: Avoid LINQ.
foreach (var targetable in Targetables)
foreach (var targetType in targetable.TargetTypes)
yield return targetType;
}
public IEnumerable<string> GetEnabledTargetTypes()
{
// PERF: Avoid LINQ.
foreach (var targetable in Targetables)
if (targetable.IsTraitEnabled())
foreach (var targetType in targetable.TargetTypes)
yield return targetType;
}
public bool IsTargetableBy(Actor byActor)
{
// PERF: Avoid LINQ.
foreach (var targetable in Targetables)
if (targetable.IsTraitEnabled() && targetable.TargetableBy(this, byActor))
return true;
return false;
}
#region Scripting interface
Lazy<ScriptActorInterface> luaInterface;
@@ -397,7 +338,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
Actor a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<Actor>(out a) || !right.TryGetClrValue<Actor>(out b))
return false;
return a == b;

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -74,7 +73,7 @@ namespace OpenRA
{
CPos a;
CVec b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<CPos>(out a) || !right.TryGetClrValue<CVec>(out b))
throw new LuaException("Attempted to call CPos.Add(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a + b);
@@ -84,7 +83,7 @@ namespace OpenRA
{
CPos a;
CVec b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<CPos>(out a) || !right.TryGetClrValue<CVec>(out b))
throw new LuaException("Attempted to call CPos.Subtract(CPos, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a - b);
@@ -93,7 +92,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CPos a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<CPos>(out a) || !right.TryGetClrValue<CPos>(out b))
return false;
return a == b;

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -76,7 +75,7 @@ namespace OpenRA
public LuaValue Add(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CVec a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<CVec>(out a) || !right.TryGetClrValue<CVec>(out b))
throw new LuaException("Attempted to call CVec.Add(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a + b);
@@ -85,7 +84,7 @@ namespace OpenRA
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CVec a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<CVec>(out a) || !right.TryGetClrValue<CVec>(out b))
throw new LuaException("Attempted to call CVec.Subtract(CVec, CVec) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, right.WrappedClrType().Name));
return new LuaCustomClrObject(a - b);
@@ -99,7 +98,7 @@ namespace OpenRA
public LuaValue Equals(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CVec a, b;
if (!left.TryGetClrValue(out a) || !right.TryGetClrValue(out b))
if (!left.TryGetClrValue<CVec>(out a) || !right.TryGetClrValue<CVec>(out b))
return false;
return a == b;
@@ -119,7 +118,7 @@ namespace OpenRA
set
{
throw new LuaException("CVec is read-only. Use CVec.New to create a new value");
throw new LuaException("WVec is read-only. Use CVec.New to create a new value");
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -16,14 +15,11 @@ namespace OpenRA
// Referenced from ModMetadata, so needs to be in OpenRA.Game :(
public class ContentInstaller : IGlobalModData
{
public enum FilenameCase { Input, ForceLower, ForceUpper }
public readonly string[] TestFiles = { };
public readonly string[] DiskTestFiles = { };
public readonly string PackageToExtractFromCD = null;
public readonly bool OverwriteFiles = true;
public readonly FilenameCase OutputFilenameCase = FilenameCase.ForceLower;
public readonly Dictionary<string, string[]> CopyFilesFromCD = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> ExtractFilesFromCD = new Dictionary<string, string[]>();

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -16,22 +15,16 @@ namespace OpenRA.Effects
{
public class SpriteEffect : IEffect
{
readonly World world;
readonly string palette;
readonly Animation anim;
readonly WPos pos;
readonly bool visibleThroughFog;
readonly bool scaleSizeWithZoom;
public SpriteEffect(WPos pos, World world, string image, string sequence, string palette, bool visibleThroughFog = false, bool scaleSizeWithZoom = false)
public SpriteEffect(WPos pos, World world, string image, string palette)
{
this.world = world;
this.pos = pos;
this.palette = palette;
this.scaleSizeWithZoom = scaleSizeWithZoom;
this.visibleThroughFog = visibleThroughFog;
anim = new Animation(world, image);
anim.PlayThen(sequence, () => world.AddFrameEndTask(w => w.Remove(this)));
anim.PlayThen("idle", () => world.AddFrameEndTask(w => w.Remove(this)));
}
public void Tick(World world)
@@ -41,11 +34,7 @@ namespace OpenRA.Effects
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
if (world.FogObscures(pos) && !visibleThroughFog)
return SpriteRenderable.None;
var zoom = scaleSizeWithZoom ? 1f / wr.Viewport.Zoom : 1f;
return anim.Render(pos, WVec.Zero, 0, wr.Palette(palette), zoom);
return anim.Render(pos, WVec.Zero, 0, wr.Palette(palette), 1f / wr.Viewport.Zoom);
}
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -114,7 +113,6 @@ namespace OpenRA
public static bool HasModifier(this Modifiers k, Modifiers mod)
{
// PERF: Enum.HasFlag is slower and requires allocations.
return (k & mod) == mod;
}
@@ -328,15 +326,6 @@ namespace OpenRA
return root;
}
public static int IntegerDivisionRoundingAwayFromZero(int dividend, int divisor)
{
int remainder;
var quotient = Math.DivRem(dividend, divisor, out remainder);
if (remainder == 0)
return quotient;
return quotient + (Math.Sign(dividend) == Math.Sign(divisor) ? 1 : -1);
}
public static string JoinWith<T>(this IEnumerable<T> ts, string j)
{
return string.Join(j, ts);
@@ -361,7 +350,7 @@ namespace OpenRA
public static Dictionary<TKey, TElement> ToDictionaryWithConflictLog<TSource, TKey, TElement>(
this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector,
string debugName, Func<TKey, string> logKey = null, Func<TElement, string> logValue = null)
string debugName, Func<TKey, string> logKey, Func<TElement, string> logValue)
{
// Fall back on ToString() if null functions are provided:
logKey = logKey ?? (s => s.ToString());

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -17,7 +16,6 @@ using System.Drawing.Imaging;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using OpenRA.Graphics;
using OpenRA.Primitives;
@@ -26,7 +24,6 @@ namespace OpenRA
{
public static class FieldLoader
{
[Serializable]
public class MissingFieldsException : YamlException
{
public readonly string[] Missing;
@@ -45,13 +42,6 @@ namespace OpenRA
Header = missing.Length > 1 ? header : headerSingle ?? header;
Missing = missing;
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("Missing", Missing);
info.AddValue("Header", Header);
}
}
public static Func<string, Type, string, object> InvalidValueAction = (s, t, f) =>
@@ -222,9 +212,21 @@ namespace OpenRA
}
else if (fieldType == typeof(Color))
{
Color color;
if (value != null && HSLColor.TryParseRGB(value, out color))
return color;
if (value != null)
{
var parts = value.Split(',');
if (parts.Length == 3)
return Color.FromArgb(
Exts.ParseIntegerInvariant(parts[0]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[1]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[2]).Clamp(0, 255));
if (parts.Length == 4)
return Color.FromArgb(
Exts.ParseIntegerInvariant(parts[0]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[1]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[2]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[3]).Clamp(0, 255));
}
return InvalidValueAction(value, fieldType, fieldName);
}
@@ -233,11 +235,20 @@ namespace OpenRA
if (value != null)
{
var parts = value.Split(',');
var colors = new Color[parts.Length];
if (parts.Length % 4 != 0)
return InvalidValueAction(value, fieldType, fieldName);
var colors = new Color[parts.Length / 4];
for (var i = 0; i < colors.Length; i++)
if (!HSLColor.TryParseRGB(parts[i], out colors[i]))
return InvalidValueAction(value, fieldType, fieldName);
{
colors[i] = Color.FromArgb(
Exts.ParseIntegerInvariant(parts[4 * i]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[4 * i + 1]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[4 * i + 2]).Clamp(0, 255),
Exts.ParseIntegerInvariant(parts[4 * i + 3]).Clamp(0, 255));
}
return colors;
}
@@ -248,12 +259,9 @@ namespace OpenRA
{
if (value != null)
{
Color rgb;
if (HSLColor.TryParseRGB(value, out rgb))
return new HSLColor(rgb);
// Allow old HSLColor/ColorRamp formats to be parsed as HSLColor
var parts = value.Split(',');
// Allow old ColorRamp format to be parsed as HSLColor
if (parts.Length == 3 || parts.Length == 4)
return new HSLColor(
(byte)Exts.ParseIntegerInvariant(parts[0]).Clamp(0, 255),
@@ -374,28 +382,6 @@ namespace OpenRA
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(CVec[]))
{
if (value != null)
{
var parts = value.Split(',');
if (parts.Length % 2 != 0)
return InvalidValueAction(value, fieldType, fieldName);
var vecs = new CVec[parts.Length / 2];
for (var i = 0; i < vecs.Length; i++)
{
int rx, ry;
if (int.TryParse(parts[2 * i], out rx) && int.TryParse(parts[2 * i + 1], out ry))
vecs[i] = new CVec(rx, ry);
}
return vecs;
}
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType.IsEnum)
{
try
@@ -736,7 +722,7 @@ namespace OpenRA
}
}
// Mirrors DescriptionAttribute from System.ComponentModel but we don't want to have to use that everywhere.
// mirrors DescriptionAttribute from System.ComponentModel but we dont want to have to use that everywhere.
[AttributeUsage(AttributeTargets.All)]
public sealed class DescAttribute : Attribute
{

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -17,7 +16,6 @@ using System.Drawing.Imaging;
using System.Globalization;
using System.Linq;
using System.Reflection;
using OpenRA.Graphics;
namespace OpenRA
{
@@ -75,16 +73,14 @@ namespace OpenRA
var t = v.GetType();
// Color.ToString() does the wrong thing; force it to format as rgb[a] hex
// Color.ToString() does the wrong thing; force it to format as an array
if (t == typeof(Color))
{
return HSLColor.ToHexString((Color)v);
}
// HSLColor.ToString() does the wrong thing; force it to format as rgb[a] hex
if (t == typeof(HSLColor))
{
return ((HSLColor)v).ToHexString();
var c = (Color)v;
return "{0},{1},{2},{3}".F(((int)c.A).Clamp(0, 255),
((int)c.R).Clamp(0, 255),
((int)c.G).Clamp(0, 255),
((int)c.B).Clamp(0, 255));
}
if (t == typeof(ImageFormat))
@@ -127,7 +123,7 @@ namespace OpenRA
return result;
}
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Primitives.Cache<,>))
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(OpenRA.Primitives.Cache<,>))
return ""; // TODO
if (t == typeof(DateTime))

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -37,88 +36,16 @@ namespace OpenRA.FileFormats
Chunk c;
c.CompressedSize = s.ReadUInt16();
c.OutputSize = s.ReadUInt16();
if (s.ReadUInt32() != 0xdeaf)
throw new InvalidDataException("Chunk header is bogus");
return c;
}
}
public class AudLoader : ISoundLoader
public static class AudLoader
{
bool IsAud(Stream s)
{
var start = s.Position;
s.Position += 10;
var readFlag = s.ReadByte();
var readFormat = s.ReadByte();
s.Position = start;
if (!Enum.IsDefined(typeof(SoundFlags), readFlag))
return false;
return Enum.IsDefined(typeof(SoundFormat), readFormat);
}
bool ISoundLoader.TryParseSound(Stream stream, out ISoundFormat sound)
{
try
{
if (IsAud(stream))
{
sound = new AudFormat(stream);
return true;
}
}
catch
{
// Not a supported AUD
}
sound = null;
return false;
}
}
public class AudFormat : ISoundFormat
{
public int Channels { get { return 1; } }
public int SampleBits { get { return 16; } }
public int SampleRate { get { return sampleRate; } }
public float LengthInSeconds { get { return AudReader.SoundLength(stream); } }
public Stream GetPCMInputStream() { return new MemoryStream(rawData.Value); }
int sampleRate;
Lazy<byte[]> rawData;
Stream stream;
public AudFormat(Stream stream)
{
this.stream = stream;
var position = stream.Position;
rawData = Exts.Lazy(() =>
{
try
{
byte[] data;
if (!AudReader.LoadSound(stream, out data, out sampleRate))
throw new InvalidDataException();
return data;
}
finally
{
stream.Position = position;
}
});
}
}
public class AudReader
{
static readonly int[] IndexAdjust = { -1, -1, -1, -1, 2, 4, 6, 8 };
static readonly int[] StepTable =
static int[] indexAdjust = { -1, -1, -1, -1, 2, 4, 6, 8 };
static int[] stepTable =
{
7, 8, 9, 10, 11, 12, 13, 14, 16,
17, 19, 21, 23, 25, 28, 31, 34, 37,
@@ -137,14 +64,14 @@ namespace OpenRA.FileFormats
var sb = (b & 8) != 0;
b &= 7;
var delta = (StepTable[index] * b) / 4 + StepTable[index] / 8;
var delta = (stepTable[index] * b) / 4 + stepTable[index] / 8;
if (sb) delta = -delta;
current += delta;
if (current > short.MaxValue) current = short.MaxValue;
if (current < short.MinValue) current = short.MinValue;
index += IndexAdjust[b];
index += indexAdjust[b];
if (index < 0) index = 0;
if (index > 88) index = 88;
@@ -190,21 +117,13 @@ namespace OpenRA.FileFormats
return samples / sampleRate;
}
public static bool LoadSound(Stream s, out byte[] rawData, out int sampleRate)
public static byte[] LoadSound(Stream s)
{
rawData = null;
sampleRate = s.ReadUInt16();
/*var sampleRate =*/ s.ReadUInt16();
var dataSize = s.ReadInt32();
var outputSize = s.ReadInt32();
var readFlag = s.ReadByte();
if (!Enum.IsDefined(typeof(SoundFlags), readFlag))
return false;
var readFormat = s.ReadByte();
if (!Enum.IsDefined(typeof(SoundFormat), readFormat))
return false;
/*var flags = (SoundFlags)*/ s.ReadByte();
/*var format = (SoundFormat)*/ s.ReadByte();
var output = new byte[outputSize];
var offset = 0;
@@ -234,8 +153,7 @@ namespace OpenRA.FileFormats
dataSize -= 8 + chunk.CompressedSize;
}
rawData = output;
return true;
return output;
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*
* This file is based on the blast routines (version 1.1 by Mark Adler)
* included in zlib/contrib
@@ -66,7 +65,7 @@ namespace OpenRA.FileFormats
var coded = br.ReadBits(8);
if (coded < 0 || coded > 1)
throw new NotImplementedException("Invalid data stream");
throw new NotImplementedException("Invalid datastream");
var encodedLiterals = coded == 1;
// log2(dictionary size) - 6
@@ -161,7 +160,7 @@ namespace OpenRA.FileFormats
return ms.ToArray();
}
// Decode a code using Huffman table h.
// Decode a code using huffman table h.
static int Decode(Huffman h, BitReader br)
{
var code = 0; // len bits being decoded

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -56,7 +55,7 @@ namespace OpenRA.FileFormats
{
var pn = (byte*)tempPn;
var i = blen * 4;
for (; i > klen; i--) pn[i - 1] = sign;
for (; i > klen; i--) pn[i - 1] = (byte)sign;
for (; i > 0; i--) pn[i - 1] = key[klen - i];
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -112,11 +111,11 @@ namespace OpenRA.FileFormats
/// <summary>
/// A fast (native) CRC32 implementation that can be used on a pinned byte array using
/// default polynomial.
/// default polynomal.
/// </summary>
/// <param name="data"> [in,out] If non-null, the.</param>
/// <param name="len"> The length of the data.</param>
/// <param name="polynomial">The polynomial to XOR with.</param>
/// <param name="len"> The length of the data data.</param>
/// <param name="polynomial">The polynomal to xor with.</param>
/// <returns>The calculated checksum.</returns>
public static unsafe uint Calculate(byte* data, uint len, uint polynomial)
{

View File

@@ -1,18 +1,16 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
namespace OpenRA.Mods.Common.FileFormats
namespace OpenRA.FileFormats
{
// Run length encoded sequences of zeros (aka Format2)
public static class RLEZerosCompression
public static class Format2
{
public static void DecodeInto(byte[] src, byte[] dest, int destIndex)
{

View File

@@ -1,18 +1,16 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
namespace OpenRA.Mods.Common.FileFormats
namespace OpenRA.FileFormats
{
// Data that is to be XORed against another set of data (aka Format40)
public static class XORDeltaCompression
public static class Format40
{
public static int DecodeInto(byte[] src, byte[] dest, int srcOffset)
{

View File

@@ -1,18 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
namespace OpenRA.Mods.Common.FileFormats
namespace OpenRA.FileFormats
{
class FastByteReader
{
@@ -42,8 +41,7 @@ namespace OpenRA.Mods.Common.FileFormats
public int Remaining() { return src.Length - offset; }
}
// Lempel - Castle - Welch algorithm (aka Format80)
public static class LCWCompression
public static class Format80
{
static void ReplicatePrevious(byte[] dest, int destIndex, int srcIndex, int count)
{
@@ -158,7 +156,7 @@ namespace OpenRA.Mods.Common.FileFormats
}
}
// Quick and dirty LCW encoder version 2
// Quick and dirty Format80 encoder version 2
// Uses raw copy and RLE compression
public static byte[] Encode(byte[] src)
{

View File

@@ -1,17 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
using System.Linq;
using OpenRA.Graphics;
namespace OpenRA.FileFormats

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,14 +1,14 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
namespace OpenRA.FileFormats

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -15,7 +14,7 @@ using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace OpenRA.Mods.Common.FileFormats
namespace OpenRA.FileFormats
{
public class IniFile
{

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -42,126 +41,114 @@ namespace OpenRA.FileFormats
Color[] palette = null;
var data = new List<byte>();
try
for (;;)
{
for (;;)
{
var length = IPAddress.NetworkToHostOrder(br.ReadInt32());
var type = Encoding.UTF8.GetString(br.ReadBytes(4));
var content = br.ReadBytes(length);
/*var crc = */br.ReadInt32();
var length = IPAddress.NetworkToHostOrder(br.ReadInt32());
var type = Encoding.UTF8.GetString(br.ReadBytes(4));
var content = br.ReadBytes(length);
/*var crc = */br.ReadInt32();
if (bitmap == null && type != "IHDR")
throw new InvalidDataException("Invalid PNG file - header does not appear first.");
using (var ms = new MemoryStream(content))
using (var cr = new BinaryReader(ms))
switch (type)
{
case "IHDR":
{
var width = IPAddress.NetworkToHostOrder(cr.ReadInt32());
var height = IPAddress.NetworkToHostOrder(cr.ReadInt32());
var bitDepth = cr.ReadByte();
var colorType = (PngColorType)cr.ReadByte();
var compression = cr.ReadByte();
/*var filter = */cr.ReadByte();
var interlace = cr.ReadByte();
using (var ms = new MemoryStream(content))
using (var cr = new BinaryReader(ms))
switch (type)
{
case "IHDR":
if (compression != 0) throw new InvalidDataException("Compression method not supported");
if (interlace != 0) throw new InvalidDataException("Interlacing not supported");
bitmap = new Bitmap(width, height, MakePixelFormat(bitDepth, colorType));
}
break;
case "PLTE":
{
palette = new Color[256];
for (var i = 0; i < 256; i++)
{
if (bitmap != null)
throw new InvalidDataException("Invalid PNG file - duplicate header.");
var width = IPAddress.NetworkToHostOrder(cr.ReadInt32());
var height = IPAddress.NetworkToHostOrder(cr.ReadInt32());
var bitDepth = cr.ReadByte();
var colorType = (PngColorType)cr.ReadByte();
var compression = cr.ReadByte();
/*var filter = */cr.ReadByte();
var interlace = cr.ReadByte();
if (compression != 0) throw new InvalidDataException("Compression method not supported");
if (interlace != 0) throw new InvalidDataException("Interlacing not supported");
bitmap = new Bitmap(width, height, MakePixelFormat(bitDepth, colorType));
var r = cr.ReadByte(); var g = cr.ReadByte(); var b = cr.ReadByte();
palette[i] = Color.FromArgb(r, g, b);
}
}
break;
break;
case "PLTE":
case "tRNS":
{
if (palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
for (var i = 0; i < length; i++)
palette[i] = Color.FromArgb(cr.ReadByte(), palette[i]);
}
break;
case "IDAT":
{
data.AddRange(content);
}
break;
case "IEND":
{
if (bitmap == null)
throw new InvalidDataException("Image header not found.");
var bits = bitmap.LockBits(bitmap.Bounds(),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
using (var ns = new MemoryStream(data.ToArray()))
{
palette = new Color[256];
for (var i = 0; i < 256; i++)
// 'zlib' flags bytes; confuses the DeflateStream.
/*var flags = (byte)*/ns.ReadByte();
/*var moreFlags = (byte)*/ns.ReadByte();
using (var ds = new DeflateStream(ns, CompressionMode.Decompress))
using (var dr = new BinaryReader(ds))
{
var r = cr.ReadByte(); var g = cr.ReadByte(); var b = cr.ReadByte();
palette[i] = Color.FromArgb(r, g, b);
}
}
break;
case "tRNS":
{
if (palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
for (var i = 0; i < length; i++)
palette[i] = Color.FromArgb(cr.ReadByte(), palette[i]);
}
break;
case "IDAT":
{
data.AddRange(content);
}
break;
case "IEND":
{
var bits = bitmap.LockBits(bitmap.Bounds(),
ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
using (var ns = new MemoryStream(data.ToArray()))
{
// 'zlib' flags bytes; confuses the DeflateStream.
/*var flags = (byte)*/ns.ReadByte();
/*var moreFlags = (byte)*/ns.ReadByte();
using (var ds = new DeflateStream(ns, CompressionMode.Decompress))
using (var dr = new BinaryReader(ds))
var prevLine = new byte[bitmap.Width]; // all zero
for (var y = 0; y < bitmap.Height; y++)
{
var prevLine = new byte[bitmap.Width]; // all zero
for (var y = 0; y < bitmap.Height; y++)
{
var filter = (PngFilter)dr.ReadByte();
var line = dr.ReadBytes(bitmap.Width);
var filter = (PngFilter)dr.ReadByte();
var line = dr.ReadBytes(bitmap.Width);
for (var i = 0; i < bitmap.Width; i++)
line[i] = i > 0
? UnapplyFilter(filter, line[i], line[i - 1], prevLine[i], prevLine[i - 1])
: UnapplyFilter(filter, line[i], 0, prevLine[i], 0);
for (var i = 0; i < bitmap.Width; i++)
line[i] = i > 0
? UnapplyFilter(filter, line[i], line[i - 1], prevLine[i], prevLine[i - 1])
: UnapplyFilter(filter, line[i], 0, prevLine[i], 0);
Marshal.Copy(line, 0, new IntPtr(bits.Scan0.ToInt64() + y * bits.Stride), line.Length);
prevLine = line;
}
Marshal.Copy(line, 0, new IntPtr(bits.Scan0.ToInt64() + y * bits.Stride), line.Length);
prevLine = line;
}
}
bitmap.UnlockBits(bits);
if (palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
using (var temp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
{
var cp = temp.Palette;
for (var i = 0; i < 256; i++)
cp.Entries[i] = palette[i]; // finalize the palette.
bitmap.Palette = cp;
return bitmap;
}
}
}
}
}
catch
{
if (bitmap != null)
bitmap.Dispose();
throw;
bitmap.UnlockBits(bits);
if (palette == null)
throw new InvalidDataException("Non-Palette indexed PNG are not supported.");
using (var temp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
{
var cp = temp.Palette;
for (var i = 0; i < 256; i++)
cp.Entries[i] = palette[i]; // finalize the palette.
bitmap.Palette = cp;
return bitmap;
}
}
}
}
}
}
@@ -200,7 +187,7 @@ namespace OpenRA.FileFormats
if (bitDepth == 8 && colorType == (PngColorType.Indexed | PngColorType.Color))
return PixelFormat.Format8bppIndexed;
throw new InvalidDataException("Unknown pixel format");
throw new InvalidDataException("Unknown pixelformat");
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,360 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace OpenRA.FileFormats
{
public class VocLoader : ISoundLoader
{
bool ISoundLoader.TryParseSound(Stream stream, out ISoundFormat sound)
{
try
{
sound = new VocFormat(stream);
return true;
}
catch
{
// Not a (supported) WAV
}
sound = null;
return false;
}
}
public class VocFormat : ISoundFormat
{
public int SampleBits { get { return 8; } }
public int Channels { get { return 1; } }
public int SampleRate { get; private set; }
public float LengthInSeconds { get { return (float)totalSamples / SampleRate; } }
public Stream GetPCMInputStream() { return new VocStream(this); }
int totalSamples = 0;
int samplePosition = 0;
Stream stream;
List<VocBlock> blocks = new List<VocBlock>();
IEnumerator<VocBlock> currentBlock;
int samplesLeftInBlock = 0;
byte[] buffer = new byte[4096];
struct VocFileHeader
{
public string Description;
public int DatablockOffset;
public int Version;
public int ID;
public static VocFileHeader Read(Stream s)
{
VocFileHeader vfh;
vfh.Description = s.ReadASCII(20);
vfh.DatablockOffset = s.ReadUInt16();
vfh.Version = s.ReadUInt16();
vfh.ID = s.ReadUInt16();
return vfh;
}
}
struct VocBlock
{
public int Code;
public int Length;
public VocSampleBlock SampleBlock;
public VocLoopBlock LoopBlock;
}
struct VocSampleBlock
{
public int Rate;
public int Samples;
public long Offset;
}
struct VocLoopBlock
{
public int Count;
}
public VocFormat(Stream stream)
{
this.stream = stream;
CheckVocHeader();
Preload();
}
void CheckVocHeader()
{
var vfh = VocFileHeader.Read(stream);
if (!vfh.Description.StartsWith("Creative Voice File"))
throw new InvalidDataException("Voc header description not recognized");
if (vfh.DatablockOffset != 26)
throw new InvalidDataException("Voc header offset is wrong");
if (vfh.Version != 0x010A)
throw new InvalidDataException("Voc header version not recognized");
if (vfh.ID != ~vfh.Version + 0x1234)
throw new InvalidDataException("Voc header id is bogus - expected: " +
(~vfh.Version + 0x1234).ToString("X") + " but value is : " + vfh.ID.ToString("X"));
}
int GetSampleRateFromVocRate(int vocSampleRate)
{
if (vocSampleRate == 256)
throw new InvalidDataException("Invalid frequency divisor 256 in voc file");
if (vocSampleRate == 0xa5 || vocSampleRate == 0xa6)
return 11025;
else if (vocSampleRate == 0xd2 || vocSampleRate == 0xd3)
return 22050;
else
return (int)(1000000L / (256L - vocSampleRate));
}
void Preload()
{
while (true)
{
VocBlock block = new VocBlock();
try
{
block.Code = stream.ReadByte();
block.Length = 0;
}
catch (EndOfStreamException)
{
// Stream is allowed to end without a last block
break;
}
if (block.Code == 0 || block.Code > 9)
break;
block.Length = stream.ReadByte();
block.Length |= stream.ReadByte() << 8;
block.Length |= stream.ReadByte() << 16;
var skip = 0;
switch (block.Code)
{
// Sound data
case 1:
{
if (block.Length < 2)
throw new InvalidDataException("Invalid sound data block length in voc file");
var freqDiv = stream.ReadByte();
block.SampleBlock.Rate = GetSampleRateFromVocRate(freqDiv);
var codec = stream.ReadByte();
if (codec != 0)
throw new InvalidDataException("Unhandled codec used in voc file");
skip = block.Length - 2;
block.SampleBlock.Samples = skip;
block.SampleBlock.Offset = stream.Position;
// See if last block contained additional information
if (blocks.Count > 0)
{
var b = blocks.Last();
if (b.Code == 8)
{
block.SampleBlock.Rate = b.SampleBlock.Rate;
blocks.Remove(b);
}
}
SampleRate = Math.Max(SampleRate, block.SampleBlock.Rate);
break;
}
// Silence
case 3:
{
if (block.Length != 3)
throw new InvalidDataException("Invalid silence block length in voc file");
block.SampleBlock.Offset = 0;
block.SampleBlock.Samples = stream.ReadUInt16() + 1;
var freqDiv = stream.ReadByte();
block.SampleBlock.Rate = GetSampleRateFromVocRate(freqDiv);
break;
}
// Repeat start
case 6:
{
if (block.Length != 2)
throw new InvalidDataException("Invalid repeat start block length in voc file");
block.LoopBlock.Count = stream.ReadUInt16() + 1;
break;
}
// Repeat end
case 7:
break;
// Extra info
case 8:
{
if (block.Length != 4)
throw new InvalidDataException("Invalid info block length in voc file");
int freqDiv = stream.ReadUInt16();
if (freqDiv == 65536)
throw new InvalidDataException("Invalid frequency divisor 65536 in voc file");
var codec = stream.ReadByte();
if (codec != 0)
throw new InvalidDataException("Unhandled codec used in voc file");
var channels = stream.ReadByte() + 1;
if (channels != 1)
throw new InvalidDataException("Unhandled number of channels in voc file");
block.SampleBlock.Offset = 0;
block.SampleBlock.Samples = 0;
block.SampleBlock.Rate = (int)(256000000L / (65536L - freqDiv));
break;
}
// Sound data (New format)
case 9:
default:
throw new InvalidDataException("Unhandled code in voc file");
}
if (skip > 0)
stream.Seek(skip, SeekOrigin.Current);
blocks.Add(block);
}
// Check validity and calculated total number of samples
foreach (var b in blocks)
{
if (b.Code == 8)
throw new InvalidDataException("Unused block 8 in voc file");
if (b.Code != 1 && b.Code != 9)
continue;
if (b.SampleBlock.Rate != SampleRate)
throw new InvalidDataException("Voc file contains chunks with different sample rate");
totalSamples += b.SampleBlock.Samples;
}
Rewind();
}
void Rewind()
{
currentBlock = blocks.GetEnumerator();
samplesLeftInBlock = 0;
samplePosition = 0;
while (currentBlock.MoveNext())
{
if (currentBlock.Current.Code == 1)
{
stream.Seek(currentBlock.Current.SampleBlock.Offset, SeekOrigin.Begin);
samplesLeftInBlock = currentBlock.Current.SampleBlock.Samples;
return;
}
}
}
bool EndOfData { get { return currentBlock.Current.Equals(blocks.Last()) && samplesLeftInBlock == 0; } }
int FillBuffer(int maxSamples)
{
var bufferedSamples = 0;
var offset = 0;
maxSamples = Math.Min(buffer.Length, maxSamples);
while (maxSamples > 0 && !EndOfData)
{
var len = Math.Min(maxSamples, samplesLeftInBlock);
stream.ReadBytes(buffer, offset, len);
offset += len;
var samplesRead = len;
bufferedSamples += samplesRead;
maxSamples -= samplesRead;
samplesLeftInBlock -= samplesRead;
samplePosition += len;
UpdateBlockIfNeeded();
}
return bufferedSamples;
}
void UpdateBlockIfNeeded()
{
if (samplesLeftInBlock == 0)
{
while (currentBlock.MoveNext())
{
if (currentBlock.Current.Code != 1 && currentBlock.Current.Code != 9)
continue;
stream.Seek(currentBlock.Current.SampleBlock.Offset, SeekOrigin.Begin);
samplesLeftInBlock = currentBlock.Current.SampleBlock.Samples;
return;
}
}
}
int Read(byte[] buffer, int offset, int count)
{
var bytesWritten = 0;
var samplesLeft = Math.Min(count, buffer.Length - offset);
while (samplesLeft > 0)
{
var len = FillBuffer(samplesLeft);
if (len == 0)
break;
Buffer.BlockCopy(this.buffer, 0, buffer, offset, len);
samplesLeft -= len;
offset += len;
bytesWritten += len;
}
return bytesWritten;
}
public class VocStream : Stream
{
VocFormat format;
public VocStream(VocFormat format)
{
this.format = format;
}
public override bool CanRead { get { return format.samplePosition < format.totalSamples; } }
public override bool CanSeek { get { return false; } }
public override bool CanWrite { get { return false; } }
public override long Length { get { return format.totalSamples; } }
public override long Position
{
get { return format.samplePosition; }
set { throw new NotImplementedException(); }
}
public override int Read(byte[] buffer, int offset, int count)
{
return format.Read(buffer, offset, count);
}
public override void Flush() { throw new NotImplementedException(); }
public override long Seek(long offset, SeekOrigin origin) { throw new NotImplementedException(); }
public override void SetLength(long value) { throw new NotImplementedException(); }
public override void Write(byte[] buffer, int offset, int count) { throw new NotImplementedException(); }
}
}
}

View File

@@ -1,19 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.IO;
using OpenRA.FileFormats;
namespace OpenRA.Mods.Common.FileFormats
namespace OpenRA.FileFormats
{
public class VqaReader
{
@@ -221,7 +219,7 @@ namespace OpenRA.Mods.Common.FileFormats
}
if (audioChannels == 1)
audioData = compressed ? AudReader.LoadSound(audio1.ToArray(), ref adpcmIndex) : audio1.ToArray();
audioData = compressed ? AudLoader.LoadSound(audio1.ToArray(), ref adpcmIndex) : audio1.ToArray();
else
{
byte[] leftData, rightData;
@@ -233,9 +231,9 @@ namespace OpenRA.Mods.Common.FileFormats
else
{
adpcmIndex = 0;
leftData = AudReader.LoadSound(audio1.ToArray(), ref adpcmIndex);
leftData = AudLoader.LoadSound(audio1.ToArray(), ref adpcmIndex);
adpcmIndex = 0;
rightData = AudReader.LoadSound(audio2.ToArray(), ref adpcmIndex);
rightData = AudLoader.LoadSound(audio2.ToArray(), ref adpcmIndex);
}
audioData = new byte[rightData.Length + leftData.Length];
@@ -331,7 +329,7 @@ namespace OpenRA.Mods.Common.FileFormats
Array.Clear(cbf, 0, cbf.Length);
Array.Clear(cbfBuffer, 0, cbfBuffer.Length);
var decodeCount = 0;
decodeCount = LCWCompression.DecodeInto(fileBuffer, cbfBuffer, decodeMode ? 1 : 0, decodeMode);
decodeCount = Format80.DecodeInto(fileBuffer, cbfBuffer, decodeMode ? 1 : 0, decodeMode);
if ((videoFlags & 0x10) == 16)
{
var p = 0;
@@ -367,7 +365,7 @@ namespace OpenRA.Mods.Common.FileFormats
if (type == "CBP0")
cbf = (byte[])cbp.Clone();
else
LCWCompression.DecodeInto(cbp, cbf);
Format80.DecodeInto(cbp, cbf);
chunkBufferOffset = currentChunkBuffer = 0;
}
@@ -392,7 +390,7 @@ namespace OpenRA.Mods.Common.FileFormats
// Frame data
case "VPTZ":
LCWCompression.DecodeInto(s.ReadBytes(subchunkLength), origData);
Format80.DecodeInto(s.ReadBytes(subchunkLength), origData);
// This is the last subchunk
return;
@@ -400,9 +398,9 @@ namespace OpenRA.Mods.Common.FileFormats
Array.Clear(origData, 0, origData.Length);
s.ReadBytes(fileBuffer, 0, subchunkLength);
if (fileBuffer[0] != 0)
vtprSize = LCWCompression.DecodeInto(fileBuffer, origData);
vtprSize = Format80.DecodeInto(fileBuffer, origData);
else
LCWCompression.DecodeInto(fileBuffer, origData, 1, true);
Format80.DecodeInto(fileBuffer, origData, 1, true);
return;
case "VPTR":
Array.Clear(origData, 0, origData.Length);

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -14,120 +13,48 @@ using System.IO;
namespace OpenRA.FileFormats
{
public class WavLoader : ISoundLoader
public class WavLoader
{
bool IsWave(Stream s)
{
var start = s.Position;
var type = s.ReadASCII(4);
s.Position += 4;
var format = s.ReadASCII(4);
s.Position = start;
public readonly int FileSize;
public readonly string Format;
return type == "RIFF" && format == "WAVE";
}
public readonly int FmtChunkSize;
public readonly int AudioFormat;
public readonly int Channels;
public readonly int SampleRate;
public readonly int ByteRate;
public readonly int BlockAlign;
public readonly int BitsPerSample;
bool ISoundLoader.TryParseSound(Stream stream, out ISoundFormat sound)
{
try
{
if (IsWave(stream))
{
sound = new WavFormat(stream);
return true;
}
}
catch
{
// Not a (supported) WAV
}
sound = null;
return false;
}
}
public class WavFormat : ISoundFormat
{
public int Channels { get { return reader.Value.Channels; } }
public int SampleBits { get { return reader.Value.BitsPerSample; } }
public int SampleRate { get { return reader.Value.SampleRate; } }
public float LengthInSeconds { get { return WavReader.WaveLength(stream); } }
public Stream GetPCMInputStream() { return new MemoryStream(reader.Value.RawOutput); }
Lazy<WavReader> reader;
readonly Stream stream;
public WavFormat(Stream stream)
{
this.stream = stream;
var position = stream.Position;
reader = Exts.Lazy(() =>
{
var wavReader = new WavReader();
try
{
if (!wavReader.LoadSound(stream))
throw new InvalidDataException();
}
finally
{
stream.Position = position;
}
return wavReader;
});
}
}
public class WavReader
{
public int FileSize;
public string Format;
public int FmtChunkSize;
public int AudioFormat;
public int Channels;
public int SampleRate;
public int ByteRate;
public int BlockAlign;
public int BitsPerSample;
public int UncompressedSize;
public int DataSize;
public byte[] RawOutput;
public readonly int UncompressedSize;
public readonly int DataSize;
public readonly byte[] RawOutput;
public enum WaveType { Pcm = 0x1, ImaAdpcm = 0x11 }
public static WaveType Type { get; private set; }
public bool LoadSound(Stream s)
public WavLoader(Stream s)
{
var type = s.ReadASCII(4);
if (type != "RIFF")
return false;
FileSize = s.ReadInt32();
Format = s.ReadASCII(4);
if (Format != "WAVE")
return false;
while (s.Position < s.Length)
{
if ((s.Position & 1) == 1)
s.ReadByte(); // Alignment
type = s.ReadASCII(4);
var type = s.ReadASCII(4);
switch (type)
{
case "RIFF":
FileSize = s.ReadInt32();
Format = s.ReadASCII(4);
if (Format != "WAVE")
throw new NotSupportedException("Not a canonical WAVE file.");
break;
case "fmt ":
FmtChunkSize = s.ReadInt32();
AudioFormat = s.ReadInt16();
Type = (WaveType)AudioFormat;
if (!Enum.IsDefined(typeof(WaveType), Type))
throw new NotSupportedException("Compression type {0} is not supported.".F(AudioFormat));
if (Type != WaveType.Pcm && Type != WaveType.ImaAdpcm)
throw new NotSupportedException("Compression type is not supported.");
Channels = s.ReadInt16();
SampleRate = s.ReadInt32();
ByteRate = s.ReadInt32();
@@ -137,17 +64,24 @@ namespace OpenRA.FileFormats
s.ReadBytes(FmtChunkSize - 16);
break;
case "fact":
var chunkSize = s.ReadInt32();
UncompressedSize = s.ReadInt32();
s.ReadBytes(chunkSize - 4);
{
var chunkSize = s.ReadInt32();
UncompressedSize = s.ReadInt32();
s.ReadBytes(chunkSize - 4);
}
break;
case "data":
DataSize = s.ReadInt32();
RawOutput = s.ReadBytes(DataSize);
break;
default:
var unknownChunkSize = s.ReadInt32();
s.ReadBytes(unknownChunkSize);
// Ignore unknown chunks
{
var chunkSize = s.ReadInt32();
s.ReadBytes(chunkSize);
}
break;
}
}
@@ -157,8 +91,6 @@ namespace OpenRA.FileFormats
RawOutput = DecodeImaAdpcmData();
BitsPerSample = 16;
}
return true;
}
public static float WaveLength(Stream s)

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -20,21 +19,22 @@ namespace OpenRA.FileFormats
public XccGlobalDatabase(Stream s)
{
var entries = new List<string>();
while (s.Peek() > -1)
var reader = new BinaryReader(s);
while (reader.PeekChar() > -1)
{
var count = s.ReadInt32();
var count = reader.ReadInt32();
for (var i = 0; i < count; i++)
{
var chars = new List<char>();
byte c;
char c;
// Read filename
while ((c = s.ReadUInt8()) != 0)
chars.Add((char)c);
while ((c = reader.ReadChar()) != 0)
chars.Add(c);
entries.Add(new string(chars.ToArray()));
// Skip comment
while ((c = s.ReadUInt8()) != 0) { }
while ((c = reader.ReadChar()) != 0) { }
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -50,11 +49,11 @@ namespace OpenRA.FileFormats
writer.Write(Encoding.ASCII.GetBytes("XCC by Olaf van der Spek"));
writer.Write(new byte[] { 0x1A, 0x04, 0x17, 0x27, 0x10, 0x19, 0x80, 0x00 });
writer.Write(Entries.Aggregate(Entries.Length, (a, b) => a + b.Length) + 52); // Size
writer.Write(0); // Type
writer.Write(0); // Version
writer.Write(0); // Game/Format (0 == TD)
writer.Write(Entries.Length); // Entries
writer.Write((int)(Entries.Aggregate(Entries.Length, (a, b) => a + b.Length) + 52)); // Size
writer.Write((int)0); // Type
writer.Write((int)0); // Version
writer.Write((int)0); // Game/Format (0 == TD)
writer.Write((int)Entries.Length); // Entries
foreach (var e in Entries)
{
writer.Write(Encoding.ASCII.GetBytes(e));

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -20,17 +19,19 @@ using OpenRA.Primitives;
namespace OpenRA.FileSystem
{
public sealed class BagFile : IReadOnlyPackage
public sealed class BagFile : IFolder
{
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
static readonly uint[] Nothing = { };
readonly string bagFilename;
readonly Stream s;
readonly Dictionary<string, IdxEntry> index;
readonly int bagFilePriority;
readonly Dictionary<uint, IdxEntry> index;
public BagFile(FileSystem context, string filename)
public BagFile(string filename, int priority)
{
Name = filename;
bagFilename = filename;
bagFilePriority = priority;
// A bag file is always accompanied with an .idx counterpart
// For example: audio.bag requires the audio.idx file
@@ -38,20 +39,23 @@ namespace OpenRA.FileSystem
// Build the index and dispose the stream, it is no longer needed after this
List<IdxEntry> entries;
using (var indexStream = context.Open(indexFilename))
using (var indexStream = GlobalFileSystem.Open(indexFilename))
entries = new IdxReader(indexStream).Entries;
index = entries.ToDictionaryWithConflictLog(x => x.Filename,
index = entries.ToDictionaryWithConflictLog(x => x.Hash,
"{0} (bag format)".F(filename),
null, x => "(offs={0}, len={1})".F(x.Offset, x.Length));
s = context.Open(filename);
s = GlobalFileSystem.Open(filename);
}
public Stream GetStream(string filename)
public int Priority { get { return 1000 + bagFilePriority; } }
public string Name { get { return bagFilename; } }
public Stream GetContent(uint hash)
{
IdxEntry entry;
if (!index.TryGetValue(filename, out entry))
if (!index.TryGetValue(hash, out entry))
return null;
s.Seek(entry.Offset, SeekOrigin.Begin);
@@ -68,7 +72,7 @@ namespace OpenRA.FileSystem
waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("WAVE"));
waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("fmt "));
waveHeaderMemoryStream.Write(BitConverter.GetBytes(16));
waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)WavReader.WaveType.Pcm));
waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)WavLoader.WaveType.Pcm));
waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)channels));
waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.SampleRate));
waveHeaderMemoryStream.Write(BitConverter.GetBytes(2 * channels * entry.SampleRate));
@@ -90,7 +94,7 @@ namespace OpenRA.FileSystem
waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("WAVE"));
waveHeaderMemoryStream.Write(Encoding.ASCII.GetBytes("fmt "));
waveHeaderMemoryStream.Write(BitConverter.GetBytes(20));
waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)WavReader.WaveType.ImaAdpcm));
waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)WavLoader.WaveType.ImaAdpcm));
waveHeaderMemoryStream.Write(BitConverter.GetBytes((short)channels));
waveHeaderMemoryStream.Write(BitConverter.GetBytes(entry.SampleRate));
waveHeaderMemoryStream.Write(BitConverter.GetBytes(bytesPerSec));
@@ -114,9 +118,65 @@ namespace OpenRA.FileSystem
return mergedStream;
}
public bool Contains(string filename)
uint? FindMatchingHash(string filename)
{
return index.ContainsKey(filename);
var hash = IdxEntry.HashFilename(filename, PackageHashType.CRC32);
if (index.ContainsKey(hash))
return hash;
// Maybe we were given a raw hash?
uint raw;
if (!uint.TryParse(filename, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out raw))
return null;
if ("{0:X}".F(raw) == filename && index.ContainsKey(raw))
return raw;
return null;
}
public Stream GetContent(string filename)
{
var hash = FindMatchingHash(filename);
return hash.HasValue ? GetContent(hash.Value) : null;
}
public bool Exists(string filename)
{
return FindMatchingHash(filename).HasValue;
}
public IEnumerable<uint> ClassicHashes()
{
return Nothing;
}
public IEnumerable<uint> CrcHashes()
{
return index.Keys;
}
public IEnumerable<string> AllFileNames()
{
var lookup = new Dictionary<uint, string>();
if (GlobalFileSystem.Exists("global mix database.dat"))
{
var db = new XccGlobalDatabase(GlobalFileSystem.Open("global mix database.dat"));
foreach (var e in db.Entries)
{
var hash = IdxEntry.HashFilename(e, PackageHashType.CRC32);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
return index.Keys.Select(k => lookup.ContainsKey(k) ? lookup[k] : "{0:X}".F(k));
}
public void Write(Dictionary<string, byte[]> contents)
{
GlobalFileSystem.Unmount(this);
throw new NotImplementedException("Updating bag files unsupported");
}
public void Dispose()

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -16,19 +15,19 @@ using System.Linq;
namespace OpenRA.FileSystem
{
public sealed class BigFile : IReadOnlyPackage
public sealed class BigFile : IFolder
{
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
readonly Dictionary<string, Entry> index = new Dictionary<string, Entry>();
public int Priority { get; private set; }
readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>();
readonly Stream s;
public BigFile(FileSystem context, string filename)
public BigFile(string filename, int priority)
{
Name = filename;
Priority = priority;
s = context.Open(filename);
s = GlobalFileSystem.Open(filename);
try
{
if (s.ReadASCII(4) != "BIGF")
@@ -48,7 +47,7 @@ namespace OpenRA.FileSystem
for (var i = 0; i < entryCount; i++)
{
var entry = new Entry(s);
index.Add(entry.Path, entry);
entries.Add(entry.Path, entry);
}
}
catch
@@ -87,14 +86,34 @@ namespace OpenRA.FileSystem
}
}
public Stream GetStream(string filename)
public Stream GetContent(string filename)
{
return index[filename].GetData();
return entries[filename].GetData();
}
public bool Contains(string filename)
public bool Exists(string filename)
{
return index.ContainsKey(filename);
return entries.ContainsKey(filename);
}
public IEnumerable<uint> ClassicHashes()
{
return entries.Keys.Select(filename => PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<uint> CrcHashes()
{
return Enumerable.Empty<uint>();
}
public IEnumerable<string> AllFileNames()
{
return entries.Keys;
}
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException();
}
public void Dispose()

View File

@@ -1,54 +1,51 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using OpenRA.Primitives;
namespace OpenRA.FileSystem
{
public sealed class D2kSoundResources : IReadOnlyPackage
public sealed class D2kSoundResources : IFolder
{
struct Entry
{
public readonly uint Offset;
public readonly uint Length;
public Entry(uint offset, uint length)
{
Offset = offset;
Length = length;
}
}
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
readonly Stream s;
readonly Dictionary<string, Entry> index = new Dictionary<string, Entry>();
public D2kSoundResources(FileSystem context, string filename)
readonly string filename;
readonly List<string> filenames;
readonly int priority;
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
public D2kSoundResources(string filename, int priority)
{
Name = filename;
this.filename = filename;
this.priority = priority;
s = context.Open(filename);
s = GlobalFileSystem.Open(filename);
try
{
filenames = new List<string>();
var headerLength = s.ReadUInt32();
while (s.Position < headerLength + 4)
{
var name = s.ReadASCIIZ();
var offset = s.ReadUInt32();
var length = s.ReadUInt32();
index.Add(name, new Entry(offset, length));
var hash = PackageEntry.HashFilename(name, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash, offset, length));
filenames.Add(name);
}
}
catch
@@ -58,19 +55,48 @@ namespace OpenRA.FileSystem
}
}
public Stream GetStream(string filename)
public Stream GetContent(uint hash)
{
Entry e;
if (!index.TryGetValue(filename, out e))
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(e.Offset, SeekOrigin.Begin);
return new MemoryStream(s.ReadBytes((int)e.Length));
}
public bool Contains(string filename)
public Stream GetContent(string filename)
{
return index.ContainsKey(filename);
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public string Name { get { return filename; } }
public int Priority { get { return 1000 + priority; } }
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save Dune 2000 Sound Resources.");
}
public void Dispose()

View File

@@ -1,301 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using OpenRA.Primitives;
namespace OpenRA.FileSystem
{
public interface IReadOnlyFileSystem
{
Stream Open(string filename);
bool TryGetPackageContaining(string path, out IReadOnlyPackage package, out string filename);
bool TryOpen(string filename, out Stream s);
bool Exists(string filename);
}
public class FileSystem : IReadOnlyFileSystem
{
public IEnumerable<IReadOnlyPackage> MountedPackages { get { return mountedPackages.Keys; } }
readonly Dictionary<IReadOnlyPackage, int> mountedPackages = new Dictionary<IReadOnlyPackage, int>();
readonly Dictionary<string, IReadOnlyPackage> explicitMounts = new Dictionary<string, IReadOnlyPackage>();
// Mod packages that should not be disposed
readonly List<IReadOnlyPackage> modPackages = new List<IReadOnlyPackage>();
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
public IReadOnlyPackage OpenPackage(string filename)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
return new MixFile(this, filename);
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
if (filename.EndsWith(".oramod", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
return new D2kSoundResources(this, filename);
if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
return new InstallShieldPackage(this, filename);
if (filename.EndsWith(".PAK", StringComparison.InvariantCultureIgnoreCase))
return new PakFile(this, filename);
if (filename.EndsWith(".big", StringComparison.InvariantCultureIgnoreCase))
return new BigFile(this, filename);
if (filename.EndsWith(".bag", StringComparison.InvariantCultureIgnoreCase))
return new BagFile(this, filename);
if (filename.EndsWith(".hdr", StringComparison.InvariantCultureIgnoreCase))
return new InstallShieldCABExtractor(this, filename);
IReadOnlyPackage parent;
string subPath = null;
if (TryGetPackageContaining(filename, out parent, out subPath))
return OpenPackage(subPath, parent);
return new Folder(Platform.ResolvePath(filename));
}
public IReadOnlyPackage OpenPackage(string filename, IReadOnlyPackage parent)
{
// HACK: limit support to zip and folder until we generalize the PackageLoader support
if (parent is Folder)
{
var path = Path.Combine(parent.Name, filename);
// HACK: work around SharpZipLib's lack of support for writing to in-memory files
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, path);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, path);
var subFolder = Platform.ResolvePath(path);
if (Directory.Exists(subFolder))
return new Folder(subFolder);
}
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename, parent.GetStream(filename));
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename, parent.GetStream(filename));
if (parent is ZipFile)
return new ZipFolder(this, (ZipFile)parent, filename, filename);
if (parent is ZipFolder)
{
var folder = (ZipFolder)parent;
return new ZipFolder(this, folder.Parent, folder.Name + "/" + filename, filename);
}
return null;
}
public IReadWritePackage OpenWritablePackage(string filename)
{
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(this, filename);
return new Folder(filename);
}
public void Mount(string name, string explicitName = null)
{
var optional = name.StartsWith("~");
if (optional)
name = name.Substring(1);
try
{
IReadOnlyPackage package;
if (name.StartsWith("$"))
{
name = name.Substring(1);
package = ModMetadata.AllMods[name].Package;
modPackages.Add(package);
}
else
package = OpenPackage(name);
Mount(package, explicitName);
}
catch
{
if (!optional)
throw;
}
}
public void Mount(IReadOnlyPackage package, string explicitName = null)
{
var mountCount = 0;
if (mountedPackages.TryGetValue(package, out mountCount))
{
// Package is already mounted
// Increment the mount count and bump up the file loading priority
mountedPackages[package] = mountCount + 1;
foreach (var filename in package.Contents)
{
fileIndex[filename].Remove(package);
fileIndex[filename].Add(package);
}
}
else
{
// Mounting the package for the first time
mountedPackages.Add(package, 1);
if (explicitName != null)
explicitMounts.Add(explicitName, package);
foreach (var filename in package.Contents)
fileIndex[filename].Add(package);
}
}
public bool Unmount(IReadOnlyPackage package)
{
var mountCount = 0;
if (!mountedPackages.TryGetValue(package, out mountCount))
return false;
if (--mountCount <= 0)
{
foreach (var packagesForFile in fileIndex.Values)
packagesForFile.RemoveAll(p => p == package);
mountedPackages.Remove(package);
var explicitKeys = explicitMounts.Where(kv => kv.Value == package)
.Select(kv => kv.Key)
.ToList();
foreach (var key in explicitKeys)
explicitMounts.Remove(key);
// Mod packages aren't owned by us, so we shouldn't dispose them
if (modPackages.Contains(package))
modPackages.Remove(package);
else
package.Dispose();
}
else
mountedPackages[package] = mountCount;
return true;
}
public void UnmountAll()
{
foreach (var package in mountedPackages.Keys)
if (!modPackages.Contains(package))
package.Dispose();
mountedPackages.Clear();
explicitMounts.Clear();
modPackages.Clear();
fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
}
public void LoadFromManifest(Manifest manifest)
{
UnmountAll();
foreach (var kv in manifest.Packages)
Mount(kv.Key, kv.Value);
}
Stream GetFromCache(string filename)
{
var package = fileIndex[filename]
.LastOrDefault(x => x.Contains(filename));
if (package != null)
return package.GetStream(filename);
return null;
}
public Stream Open(string filename)
{
Stream s;
if (!TryOpen(filename, out s))
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
return s;
}
public bool TryGetPackageContaining(string path, out IReadOnlyPackage package, out string filename)
{
var explicitSplit = path.IndexOf('|');
if (explicitSplit > 0 && explicitMounts.TryGetValue(path.Substring(0, explicitSplit), out package))
{
filename = path.Substring(explicitSplit + 1);
return true;
}
package = fileIndex[path].LastOrDefault(x => x.Contains(path));
filename = path;
return package != null;
}
public bool TryOpen(string filename, out Stream s)
{
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
{
IReadOnlyPackage explicitPackage;
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
{
s = explicitPackage.GetStream(filename.Substring(explicitSplit + 1));
if (s != null)
return true;
}
}
s = GetFromCache(filename);
if (s != null)
return true;
// Ask each package individually
// TODO: This fallback can be removed once the filesystem cleanups are complete
var package = mountedPackages.Keys.LastOrDefault(x => x.Contains(filename));
if (package != null)
{
s = package.GetStream(filename);
return s != null;
}
s = null;
return false;
}
public bool Exists(string filename)
{
var explicitSplit = filename.IndexOf('|');
if (explicitSplit > 0)
{
IReadOnlyPackage explicitPackage;
if (explicitMounts.TryGetValue(filename.Substring(0, explicitSplit), out explicitPackage))
if (explicitPackage.Contains(filename.Substring(explicitSplit + 1)))
return true;
}
return fileIndex.ContainsKey(filename);
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -14,63 +13,70 @@ using System.IO;
namespace OpenRA.FileSystem
{
public sealed class Folder : IReadWritePackage
public sealed class Folder : IFolder
{
readonly string path;
readonly int priority;
public Folder(string path)
// Create a new folder package
public Folder(string path, int priority, Dictionary<string, byte[]> contents)
{
this.path = path;
this.priority = priority;
if (Directory.Exists(path))
Directory.Delete(path, true);
Write(contents);
}
public Folder(string path, int priority)
{
this.path = path;
this.priority = priority;
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
}
public string Name { get { return path; } }
public IEnumerable<string> Contents
{
get
{
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return Path.GetFileName(filename);
foreach (var filename in Directory.GetDirectories(path))
yield return Path.GetFileName(filename);
}
}
public Stream GetStream(string filename)
public Stream GetContent(string filename)
{
try { return File.OpenRead(Path.Combine(path, filename)); }
catch { return null; }
}
public bool Contains(string filename)
public IEnumerable<uint> ClassicHashes()
{
var combined = Path.Combine(path, filename);
return combined.StartsWith(path) && File.Exists(combined);
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return PackageEntry.HashFilename(Path.GetFileName(filename), PackageHashType.Classic);
}
public void Update(string filename, byte[] contents)
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach (var filename in Directory.GetFiles(path, "*", SearchOption.TopDirectoryOnly))
yield return Path.GetFileName(filename);
}
public bool Exists(string filename)
{
return File.Exists(Path.Combine(path, filename));
}
public int Priority { get { return priority; } }
public string Name { get { return path; } }
public void Write(Dictionary<string, byte[]> contents)
{
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
using (var s = File.Create(Path.Combine(path, filename)))
s.Write(contents, 0, contents.Length);
}
public void Delete(string filename)
{
// HACK: ZipFiles can't be loaded as read-write from a stream, so we are
// forced to bypass the parent package and load them with their full path
// in FileSystem.OpenPackage. Their internal name therefore contains the
// full parent path too. We need to be careful to not add a second path
// prefix to these hacked packages.
var filePath = filename.StartsWith(path) ? filename : Path.Combine(path, filename);
if (Directory.Exists(filePath))
Directory.Delete(filePath, true);
else if (File.Exists(filePath))
File.Delete(filePath);
foreach (var file in contents)
using (var dataStream = File.Create(Path.Combine(path, file.Key)))
using (var writer = new BinaryWriter(dataStream))
writer.Write(file.Value);
}
public void Dispose() { }

View File

@@ -0,0 +1,273 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using OpenRA.Primitives;
namespace OpenRA.FileSystem
{
public interface IFolder : IDisposable
{
Stream GetContent(string filename);
bool Exists(string filename);
IEnumerable<uint> ClassicHashes();
IEnumerable<uint> CrcHashes();
IEnumerable<string> AllFileNames();
void Write(Dictionary<string, byte[]> contents);
int Priority { get; }
string Name { get; }
}
public static class GlobalFileSystem
{
public static List<IFolder> MountedFolders = new List<IFolder>();
static Cache<uint, List<IFolder>> classicHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
static Cache<uint, List<IFolder>> crcHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
public static List<string> FolderPaths = new List<string>();
static void MountInner(IFolder folder)
{
MountedFolders.Add(folder);
foreach (var hash in folder.ClassicHashes())
{
var l = classicHashIndex[hash];
if (!l.Contains(folder))
l.Add(folder);
}
foreach (var hash in folder.CrcHashes())
{
var l = crcHashIndex[hash];
if (!l.Contains(folder))
l.Add(folder);
}
}
static int order = 0;
public static IFolder CreatePackage(string filename, int order, Dictionary<string, byte[]> content)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
return new MixFile(filename, order, content);
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order, content);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order, content);
if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("The creation of .RS archives is unimplemented");
if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("The creation of .Z archives is unimplemented");
if (filename.EndsWith(".PAK", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("The creation of .PAK archives is unimplemented");
if (filename.EndsWith(".big", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("The creation of .big archives is unimplemented");
if (filename.EndsWith(".cab", StringComparison.InvariantCultureIgnoreCase))
throw new NotImplementedException("The creation of .cab archives is unimplemented");
return new Folder(filename, order, content);
}
public static IFolder OpenPackage(string filename, string annotation, int order)
{
if (filename.EndsWith(".mix", StringComparison.InvariantCultureIgnoreCase))
{
var type = string.IsNullOrEmpty(annotation)
? PackageHashType.Classic
: FieldLoader.GetValue<PackageHashType>("(value)", annotation);
return new MixFile(filename, type, order);
}
if (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order);
if (filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
return new ZipFile(filename, order);
if (filename.EndsWith(".RS", StringComparison.InvariantCultureIgnoreCase))
return new D2kSoundResources(filename, order);
if (filename.EndsWith(".Z", StringComparison.InvariantCultureIgnoreCase))
return new InstallShieldPackage(filename, order);
if (filename.EndsWith(".PAK", StringComparison.InvariantCultureIgnoreCase))
return new PakFile(filename, order);
if (filename.EndsWith(".big", StringComparison.InvariantCultureIgnoreCase))
return new BigFile(filename, order);
if (filename.EndsWith(".bag", StringComparison.InvariantCultureIgnoreCase))
return new BagFile(filename, order);
if (filename.EndsWith(".hdr", StringComparison.InvariantCultureIgnoreCase))
return new InstallShieldCABExtractor(filename, order);
return new Folder(filename, order);
}
public static void Mount(string name, string annotation = null)
{
var optional = name.StartsWith("~");
if (optional)
name = name.Substring(1);
name = Platform.ResolvePath(name);
FolderPaths.Add(name);
Action a = () => MountInner(OpenPackage(name, annotation, order++));
if (optional)
try { a(); }
catch { }
else
a();
}
public static void UnmountAll()
{
foreach (var folder in MountedFolders)
folder.Dispose();
MountedFolders.Clear();
FolderPaths.Clear();
classicHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
crcHashIndex = new Cache<uint, List<IFolder>>(_ => new List<IFolder>());
}
public static bool Unmount(IFolder mount)
{
if (MountedFolders.Contains(mount))
mount.Dispose();
return MountedFolders.RemoveAll(f => f == mount) > 0;
}
public static void Mount(IFolder mount)
{
if (!MountedFolders.Contains(mount)) MountedFolders.Add(mount);
}
public static void LoadFromManifest(Manifest manifest)
{
UnmountAll();
foreach (var dir in manifest.Folders)
Mount(dir);
foreach (var pkg in manifest.Packages)
Mount(pkg.Key, pkg.Value);
}
static Stream GetFromCache(PackageHashType type, string filename)
{
var index = type == PackageHashType.CRC32 ? crcHashIndex : classicHashIndex;
var folder = index[PackageEntry.HashFilename(filename, type)]
.Where(x => x.Exists(filename))
.MinByOrDefault(x => x.Priority);
if (folder != null)
return folder.GetContent(filename);
return null;
}
public static Stream Open(string filename)
{
Stream s;
if (!TryOpen(filename, out s))
throw new FileNotFoundException("File not found: {0}".F(filename), filename);
return s;
}
public static bool TryOpen(string name, out Stream s)
{
var filename = name;
var foldername = string.Empty;
// Used for faction specific packages; rule out false positive on Windows C:\ drive notation
var explicitFolder = name.Contains(':') && !Directory.Exists(Path.GetDirectoryName(name));
if (explicitFolder)
{
var divide = name.Split(':');
foldername = divide.First();
filename = divide.Last();
}
// Check the cache for a quick lookup if the folder name is unknown
// TODO: This disables caching for explicit folder requests
if (filename.IndexOfAny(new char[] { '/', '\\' }) == -1 && !explicitFolder)
{
s = GetFromCache(PackageHashType.Classic, filename);
if (s != null)
return true;
s = GetFromCache(PackageHashType.CRC32, filename);
if (s != null)
return true;
}
// Ask each package individually
IFolder folder;
if (explicitFolder && !string.IsNullOrEmpty(foldername))
folder = MountedFolders.Where(x => x.Name == foldername).MaxByOrDefault(x => x.Priority);
else
folder = MountedFolders.Where(x => x.Exists(filename)).MaxByOrDefault(x => x.Priority);
if (folder != null)
{
s = folder.GetContent(filename);
return true;
}
s = null;
return false;
}
public static bool Exists(string name)
{
var explicitFolder = name.Contains(':') && !Directory.Exists(Path.GetDirectoryName(name));
if (explicitFolder)
{
var divide = name.Split(':');
var foldername = divide.First();
var filename = divide.Last();
return MountedFolders.Where(n => n.Name == foldername).Any(f => f.Exists(filename));
}
else
return MountedFolders.Any(f => f.Exists(name));
}
static Dictionary<string, Assembly> assemblyCache = new Dictionary<string, Assembly>();
public static Assembly ResolveAssembly(object sender, ResolveEventArgs e)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
if (assembly.FullName == e.Name)
return assembly;
var frags = e.Name.Split(',');
var filename = frags[0] + ".dll";
Assembly a;
if (assemblyCache.TryGetValue(filename, out a))
return a;
if (Exists(filename))
using (var s = Open(filename))
{
var buf = s.ReadBytes((int)s.Length);
a = Assembly.Load(buf);
assemblyCache.Add(filename, a);
return a;
}
return null;
}
}
}

View File

@@ -1,31 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
namespace OpenRA.FileSystem
{
public interface IReadOnlyPackage : IDisposable
{
string Name { get; }
IEnumerable<string> Contents { get; }
Stream GetStream(string filename);
bool Contains(string filename);
}
public interface IReadWritePackage : IReadOnlyPackage
{
void Update(string filename, byte[] contents);
void Delete(string filename);
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -16,31 +15,80 @@ namespace OpenRA.FileSystem
{
public class IdxEntry
{
public readonly string Filename;
public const string DefaultExtension = "wav";
public readonly uint Hash;
public readonly string Name;
public readonly string Extension;
public readonly uint Offset;
public readonly uint Length;
public readonly uint SampleRate;
public readonly uint Flags;
public readonly uint ChunkSize;
public IdxEntry(uint hash, uint offset, uint length, uint sampleRate, uint flags, uint chuckSize)
{
Hash = hash;
Offset = offset;
Length = length;
SampleRate = sampleRate;
Flags = flags;
ChunkSize = chuckSize;
}
public IdxEntry(Stream s)
{
var name = s.ReadASCII(16);
var pos = name.IndexOf('\0');
if (pos != 0)
name = name.Substring(0, pos);
var asciiname = s.ReadASCII(16);
Filename = string.Concat(name, ".wav");
var pos = asciiname.IndexOf('\0');
if (pos != 0)
asciiname = asciiname.Substring(0, pos);
Name = asciiname;
Extension = DefaultExtension;
Offset = s.ReadUInt32();
Length = s.ReadUInt32();
SampleRate = s.ReadUInt32();
Flags = s.ReadUInt32();
ChunkSize = s.ReadUInt32();
Hash = HashFilename(string.Concat(Name, ".", Extension), PackageHashType.CRC32);
}
public void Write(BinaryWriter w)
{
w.Write(Name.PadRight(16, '\0'));
w.Write(Offset);
w.Write(Length);
w.Write(SampleRate);
w.Write(Flags);
w.Write(ChunkSize);
}
public override string ToString()
{
return "{0} - offset 0x{1:x8} - length 0x{2:x8}".F(Filename, Offset, Length);
string filename;
if (names.TryGetValue(Hash, out filename))
return "{0} - offset 0x{1:x8} - length 0x{2:x8}".F(filename, Offset, Length);
else
return "0x{0:x8} - offset 0x{1:x8} - length 0x{2:x8}".F(Hash, Offset, Length);
}
public static uint HashFilename(string name, PackageHashType type)
{
return PackageEntry.HashFilename(name, type);
}
static Dictionary<uint, string> names = new Dictionary<uint, string>();
public static void AddStandardName(string s)
{
// RA1 and TD
var hash = HashFilename(s, PackageHashType.Classic);
names.Add(hash, s);
// TS
var crcHash = HashFilename(s, PackageHashType.CRC32);
names.Add(crcHash, s);
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -15,10 +14,11 @@ using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
namespace OpenRA.FileSystem
{
public sealed class InstallShieldCABExtractor : IReadOnlyPackage
public class InstallShieldCABExtractor : IDisposable, IFolder
{
const uint FileSplit = 0x1;
const uint FileObfuscated = 0x2;
@@ -44,7 +44,7 @@ namespace OpenRA.FileSystem
FirstFile = reader.ReadUInt32();
LastFile = reader.ReadUInt32();
reader.Seek(offset + nameOffset, SeekOrigin.Begin);
reader.Seek(offset + (long)nameOffset, SeekOrigin.Begin);
Name = reader.ReadASCIIZ();
}
}
@@ -108,7 +108,7 @@ namespace OpenRA.FileSystem
{
Version = reader.ReadUInt32();
VolumeInfo = reader.ReadUInt32();
CabDescriptorOffset = reader.ReadUInt32();
CabDescriptorOffset = (long)reader.ReadUInt32();
CabDescriptorSize = reader.ReadUInt32();
}
}
@@ -126,7 +126,7 @@ namespace OpenRA.FileSystem
public CabDescriptor(Stream reader, CommonHeader commonHeader)
{
reader.Seek(commonHeader.CabDescriptorOffset + 12, SeekOrigin.Begin);
FileTableOffset = reader.ReadUInt32();
FileTableOffset = (long)reader.ReadUInt32();
/* unknown */ reader.ReadUInt32();
FileTableSize = reader.ReadUInt32();
@@ -135,7 +135,7 @@ namespace OpenRA.FileSystem
/* unknown */ reader.ReadBytes(8);
FileCount = reader.ReadUInt32();
FileTableOffset2 = reader.ReadUInt32();
FileTableOffset2 = (long)reader.ReadUInt32();
}
}
@@ -187,7 +187,6 @@ namespace OpenRA.FileSystem
class CabReader : IDisposable
{
readonly FileSystem context;
readonly FileDescriptor fileDes;
public uint RemainingArchiveStream;
public uint RemainingFileStream;
@@ -196,13 +195,12 @@ namespace OpenRA.FileSystem
ushort volumeNumber;
Stream cabFile;
public CabReader(FileSystem context, FileDescriptor fileDes, uint index, string commonName)
public CabReader(FileDescriptor fileDes, uint index, string commonName)
{
this.fileDes = fileDes;
this.index = index;
this.commonName = commonName;
this.context = context;
volumeNumber = (ushort)(fileDes.Volume - 1u);
volumeNumber = (ushort)((uint)fileDes.Volume - 1u);
RemainingArchiveStream = 0;
if ((fileDes.Flags & FileCompressed) > 0)
RemainingFileStream = fileDes.CompressedSize;
@@ -210,7 +208,7 @@ namespace OpenRA.FileSystem
RemainingFileStream = fileDes.ExpandedSize;
cabFile = null;
NextFile(context);
NextFile();
}
public void CopyTo(Stream dest)
@@ -260,7 +258,7 @@ namespace OpenRA.FileSystem
var read = cabFile.Read(outArray, 0, (int)RemainingArchiveStream);
if (RemainingFileStream > RemainingArchiveStream)
{
NextFile(context);
NextFile();
RemainingArchiveStream -= (uint)cabFile.Read(outArray, read, (int)count - read);
}
@@ -273,13 +271,13 @@ namespace OpenRA.FileSystem
cabFile.Dispose();
}
void NextFile(FileSystem context)
public void NextFile()
{
if (cabFile != null)
cabFile.Dispose();
++volumeNumber;
cabFile = context.Open("{0}{1}.cab".F(commonName, volumeNumber));
cabFile = GlobalFileSystem.Open("{0}{1}.cab".F(commonName, volumeNumber));
if (cabFile.ReadUInt32() != 0x28635349)
throw new InvalidDataException("Not an Installshield CAB package");
@@ -330,22 +328,23 @@ namespace OpenRA.FileSystem
readonly List<uint> directoryTable;
readonly Dictionary<uint, string> directoryNames = new Dictionary<uint, string>();
readonly Dictionary<uint, FileDescriptor> fileDescriptors = new Dictionary<uint, FileDescriptor>();
readonly Dictionary<string, uint> index = new Dictionary<string, uint>();
readonly FileSystem context;
readonly Dictionary<string, uint> fileLookup = new Dictionary<string, uint>();
int priority;
string commonName;
public int Priority { get { return priority; } }
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
public string Name { get { return commonName; } }
public InstallShieldCABExtractor(FileSystem context, string hdrFilename)
public InstallShieldCABExtractor(string hdrFilename, int priority = -1)
{
var fileGroups = new List<FileGroup>();
var fileGroupOffsets = new List<uint>();
hdrFile = context.Open(hdrFilename);
this.context = context;
this.priority = priority;
hdrFile = GlobalFileSystem.Open(hdrFilename);
// Strips archive number AND file extension
Name = Regex.Replace(hdrFilename, @"\d*\.[^\.]*$", "");
commonName = Regex.Replace(hdrFilename, @"\d*\.[^\.]*$", "");
var signature = hdrFile.ReadUInt32();
if (signature != 0x28635349)
@@ -372,7 +371,7 @@ namespace OpenRA.FileSystem
hdrFile.Seek((long)nextOffset + 4 + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
var descriptorOffset = hdrFile.ReadUInt32();
nextOffset = hdrFile.ReadUInt32();
hdrFile.Seek(descriptorOffset + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
hdrFile.Seek((long)descriptorOffset + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
fileGroups.Add(new FileGroup(hdrFile, commonHeader.CabDescriptorOffset));
}
@@ -381,12 +380,12 @@ namespace OpenRA.FileSystem
hdrFile.Seek(commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2, SeekOrigin.Begin);
foreach (var fileGroup in fileGroups)
{
for (var i = fileGroup.FirstFile; i <= fileGroup.LastFile; ++i)
for (var index = fileGroup.FirstFile; index <= fileGroup.LastFile; ++index)
{
AddFileDescriptorToList(i);
var fileDescriptor = fileDescriptors[i];
var fullFilePath = "{0}\\{1}\\{2}".F(fileGroup.Name, DirectoryName(fileDescriptor.DirectoryIndex), fileDescriptor.Filename);
index.Add(fullFilePath, i);
AddFileDescriptorToList(index);
var fileDescriptor = fileDescriptors[index];
var fullFilePath = "{0}\\{1}\\{2}".F(fileGroup.Name, DirectoryName((uint)fileDescriptor.DirectoryIndex), fileDescriptor.Filename);
fileLookup.Add(fullFilePath, index);
}
}
}
@@ -405,9 +404,9 @@ namespace OpenRA.FileSystem
return test;
}
public bool Contains(string filename)
public bool Exists(string filename)
{
return index.ContainsKey(filename);
return fileLookup.ContainsKey(filename);
}
public uint DirectoryCount()
@@ -449,6 +448,16 @@ namespace OpenRA.FileSystem
GetContentById(index, destfile);
}
public void Write(Dictionary<string, byte[]> input)
{
throw new NotImplementedException("Cannot Add Files To Cab");
}
public IEnumerable<uint> ClassicHashes()
{
return fileLookup.Keys.Select(k => PackageEntry.HashFilename(k, PackageHashType.Classic));
}
public Stream GetContentById(uint index)
{
var fileDes = fileDescriptors[index];
@@ -459,11 +468,11 @@ namespace OpenRA.FileSystem
return GetContentById(fileDes.LinkToPrevious);
if ((fileDes.Flags & FileObfuscated) != 0)
throw new NotImplementedException("Haven't implemented obfuscated files");
throw new NotImplementedException("Haven't implemented obfustcated files");
var output = new MemoryStream((int)fileDes.ExpandedSize);
using (var reader = new CabReader(context, fileDes, index, Name))
using (var reader = new CabReader(fileDes, index, commonName))
reader.CopyTo(output);
if (output.Length != fileDes.ExpandedSize)
@@ -486,18 +495,28 @@ namespace OpenRA.FileSystem
}
if ((fileDes.Flags & FileObfuscated) != 0)
throw new NotImplementedException("Haven't implemented obfuscated files");
throw new NotImplementedException("Haven't implemented obfustcated files");
using (var reader = new CabReader(context, fileDes, index, Name))
using (var reader = new CabReader(fileDes, index, commonName))
reader.CopyTo(output);
if (output.Length != fileDes.ExpandedSize)
throw new Exception("Did not fully extract Expected = {0}, Got = {1}".F(fileDes.ExpandedSize, output.Length));
}
public Stream GetStream(string fileName)
public Stream GetContent(string fileName)
{
return GetContentById(index[fileName]);
return GetContentById(fileLookup[fileName]);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
return fileLookup.Keys;
}
public void Dispose()

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -16,70 +15,54 @@ using OpenRA.FileFormats;
namespace OpenRA.FileSystem
{
public sealed class InstallShieldPackage : IReadOnlyPackage
public sealed class InstallShieldPackage : IFolder
{
struct Entry
{
public readonly uint Offset;
public readonly uint Length;
public Entry(uint offset, uint length)
{
Offset = offset;
Length = length;
}
}
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
readonly Dictionary<string, Entry> index = new Dictionary<string, Entry>();
readonly Dictionary<uint, PackageEntry> index = new Dictionary<uint, PackageEntry>();
readonly List<string> filenames;
readonly Stream s;
readonly long dataStart = 255;
readonly int priority;
readonly string filename;
public InstallShieldPackage(FileSystem context, string filename)
public InstallShieldPackage(string filename, int priority)
{
Name = filename;
this.filename = filename;
this.priority = priority;
s = context.Open(filename);
filenames = new List<string>();
s = GlobalFileSystem.Open(filename);
try
{
// Parse package header
var signature = s.ReadUInt32();
var reader = new BinaryReader(s);
var signature = reader.ReadUInt32();
if (signature != 0x8C655D13)
throw new InvalidDataException("Not an Installshield package");
s.Position += 8;
/*var FileCount = */s.ReadUInt16();
s.Position += 4;
/*var ArchiveSize = */s.ReadUInt32();
s.Position += 19;
var tocAddress = s.ReadInt32();
s.Position += 4;
var dirCount = s.ReadUInt16();
reader.ReadBytes(8);
/*var FileCount = */reader.ReadUInt16();
reader.ReadBytes(4);
/*var ArchiveSize = */reader.ReadUInt32();
reader.ReadBytes(19);
var tocAddress = reader.ReadInt32();
reader.ReadBytes(4);
var dirCount = reader.ReadUInt16();
// Parse the directory list
s.Position = tocAddress;
s.Seek(tocAddress, SeekOrigin.Begin);
var tocReader = new BinaryReader(s);
var fileCountInDirs = new List<uint>();
// Parse directories
var directories = new Dictionary<string, uint>();
for (var i = 0; i < dirCount; i++)
{
// Parse directory header
var fileCount = s.ReadUInt16();
var chunkSize = s.ReadUInt16();
var nameLength = s.ReadUInt16();
var dirName = s.ReadASCII(nameLength);
// Skip to the end of the chunk
s.ReadBytes(chunkSize - nameLength - 6);
directories.Add(dirName, fileCount);
}
fileCountInDirs.Add(ParseDirectory(tocReader));
// Parse files
foreach (var dir in directories)
for (var i = 0; i < dir.Value; i++)
ParseFile(s, dir.Key);
foreach (var fileCount in fileCountInDirs)
for (var i = 0; i < fileCount; i++)
ParseFile(reader);
}
catch
{
@@ -88,29 +71,44 @@ namespace OpenRA.FileSystem
}
}
uint accumulatedData = 0;
void ParseFile(Stream s, string dirName)
static uint ParseDirectory(BinaryReader reader)
{
s.Position += 7;
var compressedSize = s.ReadUInt32();
s.Position += 12;
var chunkSize = s.ReadUInt16();
s.Position += 4;
var nameLength = s.ReadByte();
var fileName = dirName + "\\" + s.ReadASCII(nameLength);
// Parse directory header
var fileCount = reader.ReadUInt16();
var chunkSize = reader.ReadUInt16();
var nameLength = reader.ReadUInt16();
reader.ReadChars(nameLength); // var DirName = new String(reader.ReadChars(NameLength));
// Use index syntax to overwrite any duplicate entries with the last value
index[fileName] = new Entry(accumulatedData, compressedSize);
// Skip to the end of the chunk
reader.ReadBytes(chunkSize - nameLength - 6);
return fileCount;
}
uint accumulatedData = 0;
void ParseFile(BinaryReader reader)
{
reader.ReadBytes(7);
var compressedSize = reader.ReadUInt32();
reader.ReadBytes(12);
var chunkSize = reader.ReadUInt16();
reader.ReadBytes(4);
var nameLength = reader.ReadByte();
var fileName = new string(reader.ReadChars(nameLength));
var hash = PackageEntry.HashFilename(fileName, PackageHashType.Classic);
if (!index.ContainsKey(hash))
index.Add(hash, new PackageEntry(hash, accumulatedData, compressedSize));
filenames.Add(fileName);
accumulatedData += compressedSize;
// Skip to the end of the chunk
s.Position += chunkSize - nameLength - 30;
reader.ReadBytes(chunkSize - nameLength - 30);
}
public Stream GetStream(string filename)
public Stream GetContent(uint hash)
{
Entry e;
if (!index.TryGetValue(filename, out e))
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(dataStart + e.Offset, SeekOrigin.Begin);
@@ -119,9 +117,37 @@ namespace OpenRA.FileSystem
return new MemoryStream(Blast.Decompress(data));
}
public bool Contains(string filename)
public Stream GetContent(string filename)
{
return index.ContainsKey(filename);
return GetContent(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public IEnumerable<uint> ClassicHashes()
{
return index.Keys;
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
return filenames;
}
public bool Exists(string filename)
{
return index.ContainsKey(PackageEntry.HashFilename(filename, PackageHashType.Classic));
}
public int Priority { get { return 2000 + priority; } }
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save InstallShieldPackages.");
}
public void Dispose()

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -15,26 +14,49 @@ using System.Globalization;
using System.IO;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Primitives;
namespace OpenRA.FileSystem
{
public sealed class MixFile : IReadOnlyPackage
public sealed class MixFile : IFolder
{
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
readonly Dictionary<string, PackageEntry> index;
readonly Dictionary<uint, PackageEntry> index;
readonly long dataStart;
readonly Stream s;
readonly FileSystem context;
readonly int priority;
readonly string filename;
readonly PackageHashType type;
public MixFile(FileSystem context, string filename)
// Save a mix to disk with the given contents
public MixFile(string filename, int priority, Dictionary<string, byte[]> contents)
{
Name = filename;
this.context = context;
this.filename = filename;
this.priority = priority;
this.type = PackageHashType.Classic;
s = context.Open(filename);
if (File.Exists(filename))
File.Delete(filename);
s = File.Create(filename);
try
{
index = new Dictionary<uint, PackageEntry>();
contents.Add("local mix database.dat", new XccLocalDatabase(contents.Keys.Append("local mix database.dat")).Data());
Write(contents);
}
catch
{
Dispose();
throw;
}
}
public MixFile(string filename, PackageHashType type, int priority)
{
this.filename = filename;
this.priority = priority;
this.type = type;
s = GlobalFileSystem.Open(filename);
try
{
// Detect format type
@@ -54,9 +76,9 @@ namespace OpenRA.FileSystem
else
entries = ParseHeader(s, isCncMix ? 0 : 4, out dataStart);
index = ParseIndex(entries.ToDictionaryWithConflictLog(x => x.Hash,
index = entries.ToDictionaryWithConflictLog(x => x.Hash,
"{0} ({1} format, Encrypted: {2}, DataStart: {3})".F(filename, isCncMix ? "C&C" : "RA/TS/RA2", isEncrypted, dataStart),
null, x => "(offs={0}, len={1})".F(x.Offset, x.Length)));
null, x => "(offs={0}, len={1})".F(x.Offset, x.Length));
}
catch (Exception)
{
@@ -65,58 +87,6 @@ namespace OpenRA.FileSystem
}
}
Dictionary<string, PackageEntry> ParseIndex(Dictionary<uint, PackageEntry> entries)
{
var classicIndex = new Dictionary<string, PackageEntry>();
var crcIndex = new Dictionary<string, PackageEntry>();
var allPossibleFilenames = new HashSet<string>();
// Try and find a local mix database
var dbNameClassic = PackageEntry.HashFilename("local mix database.dat", PackageHashType.Classic);
var dbNameCRC = PackageEntry.HashFilename("local mix database.dat", PackageHashType.CRC32);
foreach (var kv in entries)
{
if (kv.Key == dbNameClassic || kv.Key == dbNameCRC)
{
var db = new XccLocalDatabase(GetContent(kv.Value));
foreach (var e in db.Entries)
allPossibleFilenames.Add(e);
break;
}
}
// Load the global mix database
// TODO: This should be passed to the mix file ctor
if (context.Exists("global mix database.dat"))
{
var db = new XccGlobalDatabase(context.Open("global mix database.dat"));
foreach (var e in db.Entries)
allPossibleFilenames.Add(e);
}
foreach (var filename in allPossibleFilenames)
{
var classicHash = PackageEntry.HashFilename(filename, PackageHashType.Classic);
var crcHash = PackageEntry.HashFilename(filename, PackageHashType.CRC32);
PackageEntry e;
if (entries.TryGetValue(classicHash, out e))
classicIndex.Add(filename, e);
if (entries.TryGetValue(crcHash, out e))
crcIndex.Add(filename, e);
}
var bestIndex = crcIndex.Count > classicIndex.Count ? crcIndex : classicIndex;
var unknown = entries.Count - bestIndex.Count;
if (unknown > 0)
Log.Write("debug", "{0}: failed to resolve filenames for {1} unknown hashes".F(Name, unknown));
return bestIndex;
}
static List<PackageEntry> ParseHeader(Stream s, long offset, out long headerEnd)
{
s.Seek(offset, SeekOrigin.Begin);
@@ -186,26 +156,130 @@ namespace OpenRA.FileSystem
return ret;
}
public Stream GetContent(PackageEntry entry)
uint? FindMatchingHash(string filename)
{
Stream parentStream;
var offset = dataStart + entry.Offset + SegmentStream.GetOverallNestedOffset(s, out parentStream);
var path = ((FileStream)parentStream).Name;
return new SegmentStream(File.OpenRead(path), offset, entry.Length);
}
var hash = PackageEntry.HashFilename(filename, type);
if (index.ContainsKey(hash))
return hash;
public Stream GetStream(string filename)
{
PackageEntry e;
if (!index.TryGetValue(filename, out e))
// Maybe we were given a raw hash?
uint raw;
if (!uint.TryParse(filename, NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out raw))
return null;
return GetContent(e);
if ("{0:X}".F(raw) == filename && index.ContainsKey(raw))
return raw;
return null;
}
public bool Contains(string filename)
public Stream GetContent(uint hash)
{
return index.ContainsKey(filename);
PackageEntry e;
if (!index.TryGetValue(hash, out e))
return null;
s.Seek(dataStart + e.Offset, SeekOrigin.Begin);
var data = s.ReadBytes((int)e.Length);
return new MemoryStream(data);
}
public Stream GetContent(string filename)
{
var hash = FindMatchingHash(filename);
return hash.HasValue ? GetContent(hash.Value) : null;
}
static readonly uint[] Nothing = { };
public IEnumerable<uint> ClassicHashes()
{
if (type == PackageHashType.Classic)
return index.Keys;
return Nothing;
}
public IEnumerable<uint> CrcHashes()
{
if (type == PackageHashType.CRC32)
return index.Keys;
return Nothing;
}
public IEnumerable<string> AllFileNames()
{
var lookup = new Dictionary<uint, string>();
if (Exists("local mix database.dat"))
{
var db = new XccLocalDatabase(GetContent("local mix database.dat"));
foreach (var e in db.Entries)
{
var hash = PackageEntry.HashFilename(e, type);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
if (GlobalFileSystem.Exists("global mix database.dat"))
{
var db = new XccGlobalDatabase(GlobalFileSystem.Open("global mix database.dat"));
foreach (var e in db.Entries)
{
var hash = PackageEntry.HashFilename(e, type);
if (!lookup.ContainsKey(hash))
lookup.Add(hash, e);
}
}
return index.Keys.Select(k => lookup.ContainsKey(k) ? lookup[k] : "{0:X}".F(k));
}
public bool Exists(string filename)
{
return FindMatchingHash(filename).HasValue;
}
public int Priority { get { return 1000 + priority; } }
public string Name { get { return filename; } }
public void Write(Dictionary<string, byte[]> contents)
{
// Cannot modify existing mixfile - rename existing file and
// create a new one with original content plus modifications
GlobalFileSystem.Unmount(this);
// TODO: Add existing data to the contents list
if (index.Count > 0)
throw new NotImplementedException("Updating mix files unfinished");
// Construct a list of entries for the file header
uint dataSize = 0;
var items = new List<PackageEntry>();
foreach (var kv in contents)
{
var length = (uint)kv.Value.Length;
var hash = PackageEntry.HashFilename(Path.GetFileName(kv.Key), type);
items.Add(new PackageEntry(hash, dataSize, length));
dataSize += length;
}
// Write the new file
s.Seek(0, SeekOrigin.Begin);
using (var writer = new BinaryWriter(s))
{
// Write file header
writer.Write((ushort)items.Count);
writer.Write(dataSize);
foreach (var item in items)
item.Write(writer);
writer.Flush();
// Copy file data
foreach (var file in contents)
s.Write(file.Value);
}
}
public void Dispose()

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,14 +1,14 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
@@ -21,20 +21,20 @@ namespace OpenRA.FileSystem
public string Filename;
}
public sealed class PakFile : IReadOnlyPackage
public sealed class PakFile : IFolder
{
public string Name { get; private set; }
public IEnumerable<string> Contents { get { return index.Keys; } }
readonly string filename;
readonly int priority;
readonly Dictionary<string, Entry> index;
readonly Stream stream;
public PakFile(FileSystem context, string filename)
public PakFile(string filename, int priority)
{
Name = filename;
this.filename = filename;
this.priority = priority;
index = new Dictionary<string, Entry>();
stream = context.Open(filename);
stream = GlobalFileSystem.Open(filename);
try
{
index = new Dictionary<string, Entry>();
@@ -60,7 +60,7 @@ namespace OpenRA.FileSystem
}
}
public Stream GetStream(string filename)
public Stream GetContent(string filename)
{
Entry entry;
if (!index.TryGetValue(filename, out entry))
@@ -71,11 +71,36 @@ namespace OpenRA.FileSystem
return new MemoryStream(data);
}
public bool Contains(string filename)
public IEnumerable<uint> ClassicHashes()
{
foreach (var filename in index.Keys)
yield return PackageEntry.HashFilename(filename, PackageHashType.Classic);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach (var filename in index.Keys)
yield return filename;
}
public bool Exists(string filename)
{
return index.ContainsKey(filename);
}
public void Write(Dictionary<string, byte[]> contents)
{
throw new NotImplementedException("Cannot save Pak archives.");
}
public int Priority { get { return 1000 + priority; } }
public string Name { get { return filename; } }
public void Dispose()
{
stream.Dispose();

View File

@@ -1,14 +1,14 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
@@ -17,37 +17,46 @@ using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
namespace OpenRA.FileSystem
{
public sealed class ZipFile : IReadWritePackage
public sealed class ZipFile : IFolder
{
public string Name { get; private set; }
readonly string filename;
readonly int priority;
SZipFile pkg;
static ZipFile()
{
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
ZipConstants.DefaultCodePage = Encoding.Default.CodePage;
}
public ZipFile(FileSystem context, string filename, Stream stream, bool createOrClearContents = false)
public ZipFile(string filename, int priority)
{
Name = filename;
if (createOrClearContents)
pkg = SZipFile.Create(stream);
else
pkg = new SZipFile(stream);
this.filename = filename;
this.priority = priority;
try
{
// pull the file into memory, dont keep it open.
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
}
catch (ZipException e)
{
Log.Write("debug", "Couldn't load zip file: {0}", e.Message);
}
}
public ZipFile(IReadOnlyFileSystem context, string filename, bool createOrClearContents = false)
// Create a new zip with the specified contents
public ZipFile(string filename, int priority, Dictionary<string, byte[]> contents)
{
Name = filename;
this.priority = priority;
this.filename = filename;
if (createOrClearContents)
pkg = SZipFile.Create(filename);
else
pkg = new SZipFile(filename);
if (File.Exists(filename))
File.Delete(filename);
pkg = SZipFile.Create(filename);
Write(contents);
}
public Stream GetStream(string filename)
public Stream GetContent(string filename)
{
var entry = pkg.GetEntry(filename);
if (entry == null)
@@ -62,32 +71,44 @@ namespace OpenRA.FileSystem
}
}
public IEnumerable<string> Contents
public IEnumerable<uint> ClassicHashes()
{
get
{
foreach (ZipEntry entry in pkg)
yield return entry.Name;
}
foreach (ZipEntry entry in pkg)
yield return PackageEntry.HashFilename(entry.Name, PackageHashType.Classic);
}
public bool Contains(string filename)
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
foreach (ZipEntry entry in pkg)
yield return entry.Name;
}
public bool Exists(string filename)
{
return pkg.GetEntry(filename) != null;
}
public void Update(string filename, byte[] contents)
{
pkg.BeginUpdate();
pkg.Add(new StaticMemoryDataSource(contents), filename);
pkg.CommitUpdate();
}
public int Priority { get { return 500 + priority; } }
public string Name { get { return filename; } }
public void Delete(string filename)
public void Write(Dictionary<string, byte[]> contents)
{
// TODO: Clear existing content?
pkg.Close();
pkg = SZipFile.Create(filename);
pkg.BeginUpdate();
pkg.Delete(filename);
foreach (var kvp in contents)
pkg.Add(new StaticMemoryDataSource(kvp.Value), kvp.Key);
pkg.CommitUpdate();
pkg.Close();
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
}
public void Dispose()

View File

@@ -1,75 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using SZipFile = ICSharpCode.SharpZipLib.Zip.ZipFile;
namespace OpenRA.FileSystem
{
public sealed class ZipFolder : IReadOnlyPackage
{
public string Name { get; private set; }
public ZipFile Parent { get; private set; }
readonly string path;
static ZipFolder()
{
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
}
public ZipFolder(FileSystem context, ZipFile parent, string path, string filename)
{
if (filename.EndsWith("/"))
filename = filename.Substring(0, filename.Length - 1);
Name = filename;
Parent = parent;
if (path.EndsWith("/"))
path = path.Substring(0, path.Length - 1);
this.path = path;
}
public Stream GetStream(string filename)
{
// Zip files use '/' as a path separator
return Parent.GetStream(path + '/' + filename);
}
public IEnumerable<string> Contents
{
get
{
foreach (var entry in Parent.Contents)
{
if (entry.StartsWith(path) && entry != path)
{
var filename = entry.Substring(path.Length + 1);
var dirLevels = filename.Split('/').Count(c => !string.IsNullOrEmpty(c));
if (dirLevels == 1)
yield return filename;
}
}
}
}
public bool Contains(string filename)
{
return Parent.Contains(path + '/' + filename);
}
public void Dispose() { /* nothing to do */ }
}
}

View File

@@ -1,16 +1,14 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
@@ -19,6 +17,7 @@ using System.Linq;
using System.Net;
using System.Threading;
using OpenRA.Chat;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
@@ -47,15 +46,13 @@ namespace OpenRA
public static Sound Sound;
public static bool HasInputFocus = false;
public static bool BenchmarkMode = false;
public static GlobalChat GlobalChat;
public static OrderManager JoinServer(string host, int port, string password, bool recordReplay = true)
{
var connection = new NetworkConnection(host, port);
IConnection connection = new NetworkConnection(host, port);
if (recordReplay)
connection.StartRecording(TimestampedFilename);
connection = new ReplayRecorderConnection(connection, TimestampedFilename);
var om = new OrderManager(host, port, password, connection);
JoinInner(om);
@@ -87,7 +84,7 @@ namespace OpenRA
// More accurate replacement for Environment.TickCount
static Stopwatch stopwatch = Stopwatch.StartNew();
public static int RunTime { get { return (int)stopwatch.ElapsedMilliseconds; } }
public static int RunTime { get { return (int)Game.stopwatch.ElapsedMilliseconds; } }
public static int RenderFrame = 0;
public static int NetFrameNumber { get { return OrderManager.NetFrameNumber; } }
@@ -176,46 +173,6 @@ namespace OpenRA
GC.Collect();
}
public static void RestartGame()
{
var replay = OrderManager.Connection as ReplayConnection;
var replayName = replay != null ? replay.Filename : null;
var lobbyInfo = OrderManager.LobbyInfo;
var orders = new[] {
Order.Command("sync_lobby {0}".F(lobbyInfo.Serialize())),
Order.Command("startgame")
};
// Disconnect from the current game
Disconnect();
Ui.ResetAll();
// Restart the game with the same replay/mission
if (replay != null)
JoinReplay(replayName);
else
CreateAndStartLocalServer(lobbyInfo.GlobalSettings.Map, orders);
}
public static void CreateAndStartLocalServer(string mapUID, IEnumerable<Order> setupOrders, Action onStart = null)
{
OrderManager om = null;
Action lobbyReady = null;
lobbyReady = () =>
{
LobbyInfoChanged -= lobbyReady;
foreach (var o in setupOrders)
om.IssueOrder(o);
if (onStart != null)
onStart();
};
LobbyInfoChanged += lobbyReady;
om = JoinServer(IPAddress.Loopback.ToString(), CreateLocalServer(mapUID), "");
}
public static bool IsHost
{
get
@@ -232,13 +189,15 @@ namespace OpenRA
public static void InitializeSettings(Arguments args)
{
Settings = new Settings(Platform.ResolvePath(Path.Combine("^", "settings.yaml")), args);
Settings = new Settings(Platform.ResolvePath("^", "settings.yaml"), args);
}
internal static void Initialize(Arguments args)
{
Console.WriteLine("Platform is {0}", Platform.CurrentPlatform);
AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly;
InitializeSettings(args);
Log.AddChannel("perf", "perf.log");
@@ -260,6 +219,7 @@ namespace OpenRA
GeoIP.Initialize();
GlobalFileSystem.Mount(Platform.GameDir); // Needed to access shaders
var renderers = new[] { Settings.Graphics.Renderer, "Default", null };
foreach (var r in renderers)
{
@@ -279,7 +239,7 @@ namespace OpenRA
}
}
Sound = new Sound(Settings.Sound.Engine);
Sound = new Sound(Settings.Server.Dedicated ? "Null" : Settings.Sound.Engine);
GlobalChat = new GlobalChat();
@@ -293,18 +253,6 @@ namespace OpenRA
RunAfterDelay(Settings.Server.NatDiscoveryTimeout, UPnP.StoppingNatDiscovery);
}
public static bool IsModInstalled(string modId)
{
return ModMetadata.AllMods[modId].RequiresMods.All(IsModInstalled);
}
public static bool IsModInstalled(KeyValuePair<string, string> mod)
{
return ModMetadata.AllMods.ContainsKey(mod.Key)
&& ModMetadata.AllMods[mod.Key].Version == mod.Value
&& IsModInstalled(mod.Key);
}
public static void InitializeMod(string mod, Arguments args)
{
// Clear static state if we have switched mods
@@ -325,27 +273,30 @@ namespace OpenRA
OrderManager.Dispose();
if (ModData != null)
{
ModData.ModFiles.UnmountAll();
ModData.Dispose();
}
ModData = null;
// Fall back to default if the mod doesn't exist or has missing prerequisites.
if (!ModMetadata.AllMods.ContainsKey(mod) || !IsModInstalled(mod))
// Fall back to default if the mod doesn't exist
if (!ModMetadata.AllMods.ContainsKey(mod))
mod = new GameSettings().Mod;
Console.WriteLine("Loading mod: {0}", mod);
Settings.Game.Mod = mod;
Sound.StopVideo();
Sound.Initialize();
ModData = new ModData(mod, true);
ModData = new ModData(mod, !Settings.Server.Dedicated);
using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps();
if (Settings.Server.Dedicated)
{
RunDedicatedServer();
Environment.Exit(0);
}
var installData = ModData.Manifest.Get<ContentInstaller>();
var isModContentInstalled = installData.TestFiles.All(f => File.Exists(Platform.ResolvePath(f)));
@@ -356,8 +307,9 @@ namespace OpenRA
return;
}
ModData.InitializeLoaders(ModData.DefaultFileSystem);
Renderer.InitializeFonts(ModData);
ModData.MountFiles();
ModData.InitializeLoaders();
Renderer.InitializeFonts(ModData.Manifest);
if (Cursor != null)
Cursor.Dispose();
@@ -392,6 +344,37 @@ namespace OpenRA
ModData.LoadScreen.StartGame(args);
}
public static void RunDedicatedServer()
{
while (true)
{
Settings.Server.Map = WidgetUtils.ChooseInitialMap(Settings.Server.Map);
Settings.Save();
CreateServer(new ServerSettings(Settings.Server));
while (true)
{
Thread.Sleep(100);
if (server.State == Server.ServerState.GameStarted && server.Conns.Count < 1)
{
Console.WriteLine("No one is playing, shutting down...");
server.Shutdown();
break;
}
}
if (Settings.Server.DedicatedLoop)
{
Console.WriteLine("Starting a new server instance...");
ModData.MapCache.LoadMaps();
continue;
}
break;
}
}
public static void LoadEditor(string mapUid)
{
StartGame(mapUid, WorldType.Editor);
@@ -408,7 +391,7 @@ namespace OpenRA
static string ChooseShellmap()
{
var shellmaps = ModData.MapCache
.Where(m => m.Status == MapStatus.Available && m.Visibility.HasFlag(MapVisibility.Shellmap))
.Where(m => m.Status == MapStatus.Available && m.Map.Visibility.HasFlag(MapVisibility.Shellmap))
.Select(m => m.Uid);
if (!shellmaps.Any())
@@ -421,10 +404,10 @@ namespace OpenRA
public static event Action OnQuit = () => { };
// Note: These delayed actions should only be used by widgets or disposing objects
// - things that depend on a particular world should be queuing them on the world actor.
// - things that depend on a particular world should be queuing them on the worldactor.
static volatile ActionQueue delayedActions = new ActionQueue();
public static void RunAfterTick(Action a) { delayedActions.Add(a, RunTime); }
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, RunTime + delayMilliseconds); }
public static void RunAfterTick(Action a) { delayedActions.Add(a, Game.RunTime); }
public static void RunAfterDelay(int delayMilliseconds, Action a) { delayedActions.Add(a, Game.RunTime + delayMilliseconds); }
static void TakeScreenshotInner()
{
@@ -451,7 +434,7 @@ namespace OpenRA
bitmap.Dispose();
RunAfterTick(() => Debug("Saved screenshot " + filename));
Game.RunAfterTick(() => Debug("Saved screenshot " + filename));
});
}
@@ -490,11 +473,7 @@ namespace OpenRA
Sound.Tick();
Sync.CheckSyncUnchanged(world, orderManager.TickImmediate);
if (world == null)
return;
// Don't tick when the shellmap is disabled
if (world.ShouldTick)
if (world != null)
{
var isNetTick = LocalTick % NetTickScale == 0;
@@ -504,9 +483,6 @@ namespace OpenRA
Log.Write("debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local");
if (BenchmarkMode)
Log.Write("cpu", "{0};{1}".F(LocalTick, PerfHistory.Items["tick_time"].LastValue));
if (isNetTick)
orderManager.Tick();
@@ -520,20 +496,19 @@ namespace OpenRA
PerfHistory.Tick();
}
else if (orderManager.NetFrameNumber == 0)
orderManager.LastTickTime = RunTime;
else
if (orderManager.NetFrameNumber == 0)
orderManager.LastTickTime = RunTime;
Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer));
}
else
PerfHistory.Tick();
}
}
}
static void LogicTick()
{
delayedActions.PerformActions(RunTime);
delayedActions.PerformActions(Game.RunTime);
if (OrderManager.Connection.ConnectionState != lastConnectionState)
{
@@ -566,9 +541,9 @@ namespace OpenRA
using (new PerfSample("render_widgets"))
{
Renderer.WorldVoxelRenderer.BeginFrame();
Game.Renderer.WorldVoxelRenderer.BeginFrame();
Ui.PrepareRenderables();
Renderer.WorldVoxelRenderer.EndFrame();
Game.Renderer.WorldVoxelRenderer.EndFrame();
Ui.Draw();
@@ -593,9 +568,6 @@ namespace OpenRA
PerfHistory.Items["batches"].Tick();
PerfHistory.Items["render_widgets"].Tick();
PerfHistory.Items["render_flip"].Tick();
if (BenchmarkMode)
Log.Write("render", "{0};{1}".F(RenderFrame, PerfHistory.Items["render"].LastValue));
}
static void Loop()
@@ -646,7 +618,7 @@ namespace OpenRA
{
// Ideal time between logic updates. Timestep = 0 means the game is paused
// but we still call LogicTick() because it handles pausing internally.
var logicInterval = worldRenderer != null && worldRenderer.World.Timestep != 0 ? worldRenderer.World.Timestep : Timestep;
var logicInterval = worldRenderer != null && worldRenderer.World.Timestep != 0 ? worldRenderer.World.Timestep : Game.Timestep;
// Ideal time between screen updates
var maxFramerate = Settings.Graphics.CapFramerate ? Settings.Graphics.MaxFramerate.Clamp(1, 1000) : 1000;
@@ -774,7 +746,7 @@ namespace OpenRA
public static void CreateServer(ServerSettings settings)
{
server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData, false);
server = new Server.Server(new IPEndPoint(IPAddress.Any, settings.ListenPort), settings, ModData);
}
public static int CreateLocalServer(string map)
@@ -787,7 +759,7 @@ namespace OpenRA
AllowPortForward = false
};
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData, false);
server = new Server.Server(new IPEndPoint(IPAddress.Loopback, 0), settings, ModData);
return server.Port;
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -26,30 +25,40 @@ namespace OpenRA
/// The actor name can be anything, but the sprites used in the Render*: traits default to this one.
/// If you add an ^ in front of the name, the engine will recognize this as a collection of traits
/// that can be inherited by others (using Inherits:) and not a real unit.
/// You can remove inherited traits by adding a - in front of them as in -TraitName: to inherit everything, but this trait.
/// You can remove inherited traits by adding a - infront of them as in -TraitName: to inherit everything, but this trait.
/// </summary>
public readonly string Name;
readonly TypeDictionary traits = new TypeDictionary();
List<ITraitInfo> constructOrderCache = null;
public ActorInfo(ObjectCreator creator, string name, MiniYaml node)
public ActorInfo(string name, MiniYaml node, Dictionary<string, MiniYaml> allUnits)
{
try
{
var allParents = new HashSet<string>();
var abstractActorType = name.StartsWith("^");
// Guard against circular inheritance
allParents.Add(name);
var mergedNode = MergeWithParents(node, allUnits, allParents).ToDictionary();
Name = name;
var abstractActorType = name.StartsWith("^");
foreach (var t in node.Nodes)
foreach (var t in mergedNode)
{
try
{
traits.Add(LoadTraitInfo(creator, t.Key.Split('@')[0], t.Value));
}
catch (FieldLoader.MissingFieldsException e)
{
if (!abstractActorType)
throw new YamlException(e.Message);
}
if (t.Key[0] == '-')
throw new YamlException("Bogus trait removal: " + t.Key);
if (t.Key != "Inherits" && !t.Key.StartsWith("Inherits@"))
try
{
traits.Add(LoadTraitInfo(t.Key.Split('@')[0], t.Value));
}
catch (FieldLoader.MissingFieldsException e)
{
if (!abstractActorType)
throw new YamlException(e.Message);
}
}
}
catch (YamlException e)
@@ -65,12 +74,42 @@ namespace OpenRA
traits.Add(t);
}
static ITraitInfo LoadTraitInfo(ObjectCreator creator, string traitName, MiniYaml my)
static Dictionary<string, MiniYaml> GetParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits)
{
return node.Nodes.Where(n => n.Key == "Inherits" || n.Key.StartsWith("Inherits@"))
.ToDictionary(n => n.Value.Value, n =>
{
MiniYaml i;
if (!allUnits.TryGetValue(n.Value.Value, out i))
throw new YamlException(
"Bogus inheritance -- parent type {0} does not exist".F(n.Value.Value));
return i;
});
}
static MiniYaml MergeWithParents(MiniYaml node, Dictionary<string, MiniYaml> allUnits, HashSet<string> allParents)
{
var parents = GetParents(node, allUnits);
foreach (var kv in parents)
{
if (!allParents.Add(kv.Key))
throw new YamlException(
"Bogus inheritance -- duplicate inheritance of {0}.".F(kv.Key));
node = MiniYaml.MergeStrict(node, MergeWithParents(kv.Value, allUnits, allParents));
}
return node;
}
static ITraitInfo LoadTraitInfo(string traitName, MiniYaml my)
{
if (!string.IsNullOrEmpty(my.Value))
throw new YamlException("Junk value `{0}` on trait node {1}"
.F(my.Value, traitName));
var info = creator.CreateObject<ITraitInfo>(traitName + "Info");
var info = Game.CreateObject<ITraitInfo>(traitName + "Info");
try
{
FieldLoader.Load(info, my);
@@ -100,15 +139,9 @@ namespace OpenRA
var unresolved = source.Except(resolved);
var testResolve = new Func<Type, Type, bool>((a, b) => a == b || a.IsAssignableFrom(b));
var more = unresolved.Where(u => u.Dependencies.All(d => resolved.Exists(r => testResolve(d, r.Type))));
// This query detects which unresolved traits can be immediately resolved as all their direct dependencies are met.
var more = unresolved.Where(u =>
u.Dependencies.All(d => // To be resolvable, all dependencies must be satisfied according to the following conditions:
resolved.Exists(r => testResolve(d, r.Type)) && // There must exist a resolved trait that meets the dependency.
!unresolved.Any(u1 => testResolve(d, u1.Type)))); // All matching traits that meet this dependency must be resolved first.
// Continue resolving traits as long as possible.
// Each time we resolve some traits, this means dependencies for other traits may then be possible to satisfy in the next pass.
// Re-evaluate the vars above until sorted
while (more.Any())
resolved.AddRange(more);
@@ -128,14 +161,14 @@ namespace OpenRA
exceptionString += u.Type + ": { " + string.Join(", ", deps) + " }\r\n";
}
throw new YamlException(exceptionString);
throw new Exception(exceptionString);
}
constructOrderCache = resolved.Select(r => r.Trait).ToList();
return constructOrderCache;
}
public static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
{
return info
.GetType()
@@ -158,9 +191,9 @@ namespace OpenRA
i.Name.Replace("Init", ""), i));
}
public bool HasTraitInfo<T>() where T : ITraitInfoInterface { return traits.Contains<T>(); }
public T TraitInfo<T>() where T : ITraitInfoInterface { return traits.Get<T>(); }
public T TraitInfoOrDefault<T>() where T : ITraitInfoInterface { return traits.GetOrDefault<T>(); }
public IEnumerable<T> TraitInfos<T>() where T : ITraitInfoInterface { return traits.WithInterface<T>(); }
public bool HasTraitInfo<T>() where T : ITraitInfo { return traits.Contains<T>(); }
public T TraitInfo<T>() where T : ITraitInfo { return traits.Get<T>(); }
public T TraitInfoOrDefault<T>() where T : ITraitInfo { return traits.GetOrDefault<T>(); }
public IEnumerable<T> TraitInfos<T>() where T : ITraitInfo { return traits.WithInterface<T>(); }
}
}

View File

@@ -1,15 +1,13 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.IO;
using OpenRA.FileFormats;
using OpenRA.FileSystem;
@@ -36,24 +34,19 @@ namespace OpenRA.GameRules
Filename = (nd.ContainsKey("Filename") ? nd["Filename"].Value : key) + "." + ext;
}
public void Load(IReadOnlyFileSystem fileSystem)
public void Load()
{
Stream stream;
if (!fileSystem.TryOpen(Filename, out stream))
if (!GlobalFileSystem.Exists(Filename))
return;
Exists = true;
ISoundFormat soundFormat;
foreach (var loader in Game.ModData.SoundLoaders)
using (var s = GlobalFileSystem.Open(Filename))
{
if (loader.TryParseSound(stream, out soundFormat))
{
Length = (int)soundFormat.LengthInSeconds;
break;
}
if (Filename.ToLowerInvariant().EndsWith("wav"))
Length = (int)WavLoader.WaveLength(s);
else
Length = (int)AudLoader.SoundLength(s);
}
stream.Dispose();
}
}
}

View File

@@ -1,19 +1,15 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using OpenRA.FileSystem;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Traits;
@@ -27,25 +23,25 @@ namespace OpenRA
public readonly IReadOnlyDictionary<string, SoundInfo> Voices;
public readonly IReadOnlyDictionary<string, SoundInfo> Notifications;
public readonly IReadOnlyDictionary<string, MusicInfo> Music;
public readonly TileSet TileSet;
public readonly SequenceProvider Sequences;
public readonly IReadOnlyDictionary<string, TileSet> TileSets;
public readonly IReadOnlyDictionary<string, SequenceProvider> Sequences;
public Ruleset(
IReadOnlyDictionary<string, ActorInfo> actors,
IReadOnlyDictionary<string, WeaponInfo> weapons,
IReadOnlyDictionary<string, SoundInfo> voices,
IReadOnlyDictionary<string, SoundInfo> notifications,
IReadOnlyDictionary<string, MusicInfo> music,
TileSet tileSet,
SequenceProvider sequences)
IDictionary<string, ActorInfo> actors,
IDictionary<string, WeaponInfo> weapons,
IDictionary<string, SoundInfo> voices,
IDictionary<string, SoundInfo> notifications,
IDictionary<string, MusicInfo> music,
IDictionary<string, TileSet> tileSets,
IDictionary<string, SequenceProvider> sequences)
{
Actors = actors;
Weapons = weapons;
Voices = voices;
Notifications = notifications;
Music = music;
TileSet = tileSet;
Sequences = sequences;
Actors = new ReadOnlyDictionary<string, ActorInfo>(actors);
Weapons = new ReadOnlyDictionary<string, WeaponInfo>(weapons);
Voices = new ReadOnlyDictionary<string, SoundInfo>(voices);
Notifications = new ReadOnlyDictionary<string, SoundInfo>(notifications);
Music = new ReadOnlyDictionary<string, MusicInfo>(music);
TileSets = new ReadOnlyDictionary<string, TileSet>(tileSets);
Sequences = new ReadOnlyDictionary<string, SequenceProvider>(sequences);
foreach (var a in Actors.Values)
{
@@ -83,172 +79,5 @@ namespace OpenRA
}
public IEnumerable<KeyValuePair<string, MusicInfo>> InstalledMusic { get { return Music.Where(m => m.Value.Exists); } }
static IReadOnlyDictionary<string, T> MergeOrDefault<T>(string name, IReadOnlyFileSystem fileSystem, IEnumerable<string> files, MiniYaml additional,
IReadOnlyDictionary<string, T> defaults, Func<MiniYamlNode, T> makeObject)
{
if (additional == null && defaults != null)
return defaults;
var result = MiniYaml.Load(fileSystem, files, additional)
.ToDictionaryWithConflictLog(k => k.Key.ToLowerInvariant(), makeObject, "LoadFromManifest<" + name + ">");
return new ReadOnlyDictionary<string, T>(result);
}
public static Ruleset LoadDefaults(ModData modData)
{
var m = modData.Manifest;
var fs = modData.DefaultFileSystem;
Ruleset ruleset = null;
Action f = () =>
{
var actors = MergeOrDefault("Manifest,Rules", fs, m.Rules, null, null,
k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value));
var weapons = MergeOrDefault("Manifest,Weapons", fs, m.Weapons, null, null,
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
var voices = MergeOrDefault("Manifest,Voices", fs, m.Voices, null, null,
k => new SoundInfo(k.Value));
var notifications = MergeOrDefault("Manifest,Notifications", fs, m.Notifications, null, null,
k => new SoundInfo(k.Value));
var music = MergeOrDefault("Manifest,Music", fs, m.Music, null, null,
k => new MusicInfo(k.Key, k.Value));
// The default ruleset does not include a preferred tileset or sequence set
ruleset = new Ruleset(actors, weapons, voices, notifications, music, null, null);
};
if (modData.IsOnMainThread)
{
modData.HandleLoadingProgress();
var loader = new Task(f);
loader.Start();
// Animate the loadscreen while we wait
while (!loader.Wait(40))
modData.HandleLoadingProgress();
}
else
f();
return ruleset;
}
public static Ruleset LoadDefaultsForTileSet(ModData modData, string tileSet)
{
var dr = modData.DefaultRules;
var ts = modData.DefaultTileSets[tileSet];
var sequences = modData.DefaultSequences[tileSet];
return new Ruleset(dr.Actors, dr.Weapons, dr.Voices, dr.Notifications, dr.Music, ts, sequences);
}
public static Ruleset Load(ModData modData, IReadOnlyFileSystem fileSystem, string tileSet,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications,
MiniYaml mapMusic, MiniYaml mapSequences)
{
var m = modData.Manifest;
var dr = modData.DefaultRules;
Ruleset ruleset = null;
Action f = () =>
{
var actors = MergeOrDefault("Rules", fileSystem, m.Rules, mapRules, dr.Actors,
k => new ActorInfo(modData.ObjectCreator, k.Key.ToLowerInvariant(), k.Value));
var weapons = MergeOrDefault("Weapons", fileSystem, m.Weapons, mapWeapons, dr.Weapons,
k => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
var voices = MergeOrDefault("Voices", fileSystem, m.Voices, mapVoices, dr.Voices,
k => new SoundInfo(k.Value));
var notifications = MergeOrDefault("Notifications", fileSystem, m.Notifications, mapNotifications, dr.Notifications,
k => new SoundInfo(k.Value));
var music = MergeOrDefault("Music", fileSystem, m.Music, mapMusic, dr.Music,
k => new MusicInfo(k.Key, k.Value));
// TODO: Add support for merging custom tileset modifications
var ts = modData.DefaultTileSets[tileSet];
// TODO: Top-level dictionary should be moved into the Ruleset instead of in its own object
var sequences = mapSequences == null ? modData.DefaultSequences[tileSet] :
new SequenceProvider(fileSystem, modData, ts, mapSequences);
// TODO: Add support for custom voxel sequences
ruleset = new Ruleset(actors, weapons, voices, notifications, music, ts, sequences);
};
if (modData.IsOnMainThread)
{
modData.HandleLoadingProgress();
var loader = new Task(f);
loader.Start();
// Animate the loadscreen while we wait
while (!loader.Wait(40))
modData.HandleLoadingProgress();
}
else
f();
return ruleset;
}
static bool AnyCustomYaml(MiniYaml yaml)
{
return yaml != null && (yaml.Value != null || yaml.Nodes.Any());
}
static bool AnyFlaggedTraits(ModData modData, List<MiniYamlNode> actors)
{
foreach (var actorNode in actors)
{
foreach (var traitNode in actorNode.Value.Nodes)
{
try
{
var traitName = traitNode.Key.Split('@')[0];
var traitType = modData.ObjectCreator.FindType(traitName + "Info");
if (traitType.GetInterface("ILobbyCustomRulesIgnore") == null)
return true;
}
catch { }
}
}
return false;
}
public static bool DefinesUnsafeCustomRules(ModData modData, IReadOnlyFileSystem fileSystem,
MiniYaml mapRules, MiniYaml mapWeapons, MiniYaml mapVoices, MiniYaml mapNotifications, MiniYaml mapSequences)
{
// Maps that define any weapon, voice, notification, or sequence overrides are always flagged
if (AnyCustomYaml(mapWeapons) || AnyCustomYaml(mapVoices) || AnyCustomYaml(mapNotifications) || AnyCustomYaml(mapSequences))
return true;
// Any trait overrides that aren't explicitly whitelisted are flagged
if (mapRules != null)
{
if (AnyFlaggedTraits(modData, mapRules.Nodes))
return true;
if (mapRules.Value != null)
{
var mapFiles = FieldLoader.GetValue<string[]>("value", mapRules.Value);
foreach (var f in mapFiles)
if (AnyFlaggedTraits(modData, MiniYaml.FromStream(fileSystem.Open(f))))
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,159 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Graphics;
using OpenRA.Support;
namespace OpenRA
{
public sealed class RulesetCache : IDisposable
{
static readonly List<MiniYamlNode> NoMapRules = new List<MiniYamlNode>();
readonly ModData modData;
readonly Dictionary<string, ActorInfo> actorCache = new Dictionary<string, ActorInfo>();
readonly Dictionary<string, WeaponInfo> weaponCache = new Dictionary<string, WeaponInfo>();
readonly Dictionary<string, SoundInfo> voiceCache = new Dictionary<string, SoundInfo>();
readonly Dictionary<string, SoundInfo> notificationCache = new Dictionary<string, SoundInfo>();
readonly Dictionary<string, MusicInfo> musicCache = new Dictionary<string, MusicInfo>();
readonly Dictionary<string, TileSet> tileSetCache = new Dictionary<string, TileSet>();
readonly Dictionary<string, SequenceCache> sequenceCaches = new Dictionary<string, SequenceCache>();
public event EventHandler LoadingProgress;
void RaiseProgress()
{
if (LoadingProgress != null)
LoadingProgress(this, new EventArgs());
}
public RulesetCache(ModData modData)
{
this.modData = modData;
}
/// <summary>
/// Cache and return the Ruleset for a given map.
/// If a map isn't specified then return the default mod Ruleset.
/// </summary>
public Ruleset Load(Map map = null)
{
var m = modData.Manifest;
Dictionary<string, ActorInfo> actors;
Dictionary<string, WeaponInfo> weapons;
Dictionary<string, SoundInfo> voices;
Dictionary<string, SoundInfo> notifications;
Dictionary<string, MusicInfo> music;
Dictionary<string, TileSet> tileSets;
using (new PerfTimer("Actors"))
actors = LoadYamlRules(actorCache, m.Rules,
map != null ? map.RuleDefinitions : NoMapRules,
(k, y) => new ActorInfo(k.Key.ToLowerInvariant(), k.Value, y));
using (new PerfTimer("Weapons"))
weapons = LoadYamlRules(weaponCache, m.Weapons,
map != null ? map.WeaponDefinitions : NoMapRules,
(k, _) => new WeaponInfo(k.Key.ToLowerInvariant(), k.Value));
using (new PerfTimer("Voices"))
voices = LoadYamlRules(voiceCache, m.Voices,
map != null ? map.VoiceDefinitions : NoMapRules,
(k, _) => new SoundInfo(k.Value));
using (new PerfTimer("Notifications"))
notifications = LoadYamlRules(notificationCache, m.Notifications,
map != null ? map.NotificationDefinitions : NoMapRules,
(k, _) => new SoundInfo(k.Value));
using (new PerfTimer("Music"))
music = LoadYamlRules(musicCache, m.Music,
map != null ? map.MusicDefinitions : NoMapRules,
(k, _) => new MusicInfo(k.Key, k.Value));
using (new PerfTimer("TileSets"))
tileSets = LoadTileSets(tileSetCache, sequenceCaches, m.TileSets);
var sequences = sequenceCaches.ToDictionary(kvp => kvp.Key, kvp => new SequenceProvider(kvp.Value, map));
return new Ruleset(actors, weapons, voices, notifications, music, tileSets, sequences);
}
Dictionary<string, T> LoadYamlRules<T>(
Dictionary<string, T> itemCache,
string[] files, List<MiniYamlNode> nodes,
Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> f)
{
RaiseProgress();
var inputKey = string.Concat(string.Join("|", files), "|", nodes.WriteToString());
var mergedNodes = files
.Select(s => MiniYaml.FromFile(s))
.Aggregate(nodes, MiniYaml.MergeLiberal);
Func<MiniYamlNode, Dictionary<string, MiniYaml>, T> wrap = (wkv, wyy) =>
{
var key = inputKey + wkv.Value.ToLines(wkv.Key).JoinWith("|");
T t;
if (itemCache.TryGetValue(key, out t))
return t;
t = f(wkv, wyy);
itemCache.Add(key, t);
RaiseProgress();
return t;
};
var yy = mergedNodes.ToDictionary(x => x.Key, x => x.Value);
var itemSet = mergedNodes.ToDictionaryWithConflictLog(kv => kv.Key.ToLowerInvariant(), kv => wrap(kv, yy), "LoadYamlRules", null, null);
RaiseProgress();
return itemSet;
}
Dictionary<string, TileSet> LoadTileSets(Dictionary<string, TileSet> itemCache, Dictionary<string, SequenceCache> sequenceCaches, string[] files)
{
var items = new Dictionary<string, TileSet>();
foreach (var file in files)
{
TileSet t;
if (itemCache.TryGetValue(file, out t))
items.Add(t.Id, t);
else
{
t = new TileSet(modData, file);
itemCache.Add(file, t);
// every time we load a tile set, we create a sequence cache for it
var sc = new SequenceCache(modData, t);
sequenceCaches.Add(t.Id, sc);
items.Add(t.Id, t);
}
}
return items;
}
public void Dispose()
{
foreach (var cache in sequenceCaches.Values)
cache.Dispose();
sequenceCaches.Clear();
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -25,7 +24,6 @@ namespace OpenRA.GameRules
public int[] RangeModifiers;
public int Facing;
public WPos Source;
public Func<WPos> CurrentSource;
public Actor SourceActor;
public WPos PassiveTarget;
public Target GuidedTarget;
@@ -47,6 +45,8 @@ namespace OpenRA.GameRules
[Desc("Number of shots in a single ammo magazine.")]
public readonly int Burst = 1;
public readonly bool Charges = false;
[Desc("What types of targets are affected.")]
public readonly HashSet<string> ValidTargets = new HashSet<string> { "Ground", "Water" };
@@ -126,17 +126,14 @@ namespace OpenRA.GameRules
/// <summary>Checks if the weapon is valid against (can target) the actor.</summary>
public bool IsValidAgainst(Actor victim, Actor firedBy)
{
var targetTypes = victim.GetEnabledTargetTypes();
if (!IsValidTarget(targetTypes))
var targetable = victim.TraitsImplementing<ITargetable>().Where(Exts.IsTraitEnabled);
if (!IsValidTarget(targetable.SelectMany(t => t.TargetTypes)))
return false;
// PERF: Avoid LINQ.
foreach (var warhead in Warheads)
if (warhead.IsValidAgainst(victim, firedBy))
return true;
if (!Warheads.Any(w => w.IsValidAgainst(victim, firedBy)))
return false;
return false;
return true;
}
/// <summary>Checks if the weapon is valid against (can target) the frozen actor.</summary>

View File

@@ -1,15 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
namespace OpenRA
{

View File

@@ -1,19 +1,20 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Meebey.SmartIrc4net;
using OpenRA;
using OpenRA.Primitives;
namespace OpenRA.Chat
@@ -65,7 +66,7 @@ namespace OpenRA.Chat
}
}
public sealed class GlobalChat : IDisposable
public class GlobalChat : IDisposable
{
readonly IrcClient client = new IrcClient();
volatile Channel channel;
@@ -106,8 +107,6 @@ namespace OpenRA.Chat
client.OnDevoice += (_, e) => SetUserVoiced(e.Whom, false);
client.OnPart += OnPart;
client.OnQuit += OnQuit;
TrySetNickname(Game.Settings.Player.Name);
}
void SetUserOp(string whom, bool isOp)
@@ -226,22 +225,14 @@ namespace OpenRA.Chat
void OnKick(object sender, KickEventArgs e)
{
if (e.Whom == client.Nickname)
{
Disconnect();
connectionStatus = ChatConnectionStatus.Error;
AddNotification("You were kicked from the chat by {0}. ({1})".F(e.Who, e.KickReason));
}
else
{
Users.Remove(e.Whom);
AddNotification("{0} was kicked from the chat by {1}. ({2})".F(e.Whom, e.Who, e.KickReason));
}
Disconnect();
connectionStatus = ChatConnectionStatus.Error;
AddNotification("Error: You were kicked from the chat by {0}".F(e.Who));
}
void OnJoin(object sender, JoinEventArgs e)
{
if (e.Who == client.Nickname || channel == null || e.Channel != channel.Name)
if (e.Who == client.Nickname || e.Channel != channel.Name)
return;
AddNotification("{0} joined the chat.".F(e.Who));
@@ -284,7 +275,7 @@ namespace OpenRA.Chat
void OnPart(object sender, PartEventArgs e)
{
if (channel == null || e.Data.Channel != channel.Name)
if (e.Data.Channel != channel.Name)
return;
AddNotification("{0} left the chat.".F(e.Who));

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -18,35 +17,36 @@ namespace OpenRA.Graphics
{
public class Animation
{
readonly int defaultTick = 40; // 25 fps == 40 ms
public ISpriteSequence CurrentSequence { get; private set; }
public string Name { get; private set; }
public bool IsDecoration { get; set; }
public bool IsDecoration = false;
public Func<bool> Paused;
readonly Func<int> facingFunc;
int frame = 0;
bool backwards = false;
string name;
bool tickAlways;
public string Name { get { return name; } }
readonly SequenceProvider sequenceProvider;
readonly Func<int> facingFunc;
readonly Func<bool> paused;
int frame;
bool backwards;
bool tickAlways;
int timeUntilNextFrame;
Action tickFunc = () => { };
public Animation(World world, string name)
: this(world, name, () => 0) { }
public Animation(World world, string name, Func<int> facingFunc)
: this(world, name, facingFunc, null) { }
: this(world.Map.SequenceProvider, name, facingFunc) { }
public Animation(World world, string name, Func<bool> paused)
: this(world, name, () => 0, paused) { }
public Animation(World world, string name, Func<int> facingFunc, Func<bool> paused)
public Animation(SequenceProvider sequenceProvider, string name, Func<int> facingFunc)
{
sequenceProvider = world.Map.Rules.Sequences;
Name = name.ToLowerInvariant();
this.sequenceProvider = sequenceProvider;
this.name = name.ToLowerInvariant();
this.tickFunc = () => { };
this.facingFunc = facingFunc;
this.paused = paused;
}
public int CurrentFrame { get { return backwards ? CurrentSequence.Start + CurrentSequence.Length - frame - 1 : frame; } }
@@ -76,23 +76,12 @@ namespace OpenRA.Graphics
PlayThen(sequenceName, null);
}
int CurrentSequenceTickOrDefault()
{
const int DefaultTick = 40; // 25 fps == 40 ms
return CurrentSequence != null ? CurrentSequence.Tick : DefaultTick;
}
void PlaySequence(string sequenceName)
{
CurrentSequence = GetSequence(sequenceName);
timeUntilNextFrame = CurrentSequenceTickOrDefault();
}
public void PlayRepeating(string sequenceName)
{
backwards = false;
tickAlways = false;
PlaySequence(sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = 0;
tickFunc = () =>
@@ -108,8 +97,9 @@ namespace OpenRA.Graphics
if (!HasSequence(sequenceName))
return false;
CurrentSequence = GetSequence(sequenceName);
timeUntilNextFrame = Math.Min(CurrentSequenceTickOrDefault(), timeUntilNextFrame);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
var tick = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
timeUntilNextFrame = Math.Min(tick, timeUntilNextFrame);
frame %= CurrentSequence.Length;
return true;
}
@@ -118,7 +108,8 @@ namespace OpenRA.Graphics
{
backwards = false;
tickAlways = false;
PlaySequence(sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = 0;
tickFunc = () =>
@@ -143,7 +134,8 @@ namespace OpenRA.Graphics
{
backwards = false;
tickAlways = true;
PlaySequence(sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = func();
tickFunc = () => frame = func();
@@ -152,7 +144,8 @@ namespace OpenRA.Graphics
public void PlayFetchDirection(string sequenceName, Func<int> direction)
{
tickAlways = false;
PlaySequence(sequenceName);
CurrentSequence = sequenceProvider.GetSequence(name, sequenceName);
timeUntilNextFrame = CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
frame = 0;
tickFunc = () =>
@@ -166,12 +159,17 @@ namespace OpenRA.Graphics
};
}
int timeUntilNextFrame;
Action tickFunc;
public void Tick()
{
if (paused == null || !paused())
if (Paused == null || !Paused())
Tick(40); // tick one frame
}
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(name, seq); }
public void Tick(int t)
{
if (tickAlways)
@@ -182,7 +180,7 @@ namespace OpenRA.Graphics
while (timeUntilNextFrame <= 0)
{
tickFunc();
timeUntilNextFrame += CurrentSequenceTickOrDefault();
timeUntilNextFrame += CurrentSequence != null ? CurrentSequence.Tick : defaultTick;
}
}
}
@@ -191,19 +189,17 @@ namespace OpenRA.Graphics
{
newImage = newImage.ToLowerInvariant();
if (Name != newImage)
if (name != newImage)
{
Name = newImage;
name = newImage.ToLowerInvariant();
if (!ReplaceAnim(CurrentSequence.Name))
ReplaceAnim(newAnimIfMissing);
}
}
public bool HasSequence(string seq) { return sequenceProvider.HasSequence(Name, seq); }
public ISpriteSequence GetSequence(string sequenceName)
{
return sequenceProvider.GetSequence(Name, sequenceName);
return sequenceProvider.GetSequence(name, sequenceName);
}
public string GetRandomExistingSequence(string[] sequences, MersenneTwister random)

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -19,20 +18,22 @@ namespace OpenRA.Graphics
public readonly Animation Animation;
public readonly Func<WVec> OffsetFunc;
public readonly Func<bool> DisableFunc;
public readonly Func<bool> Paused;
public readonly Func<WPos, int> ZOffset;
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable)
: this(a, offset, disable, null) { }
: this(a, offset, disable, () => false, null) { }
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, int zOffset)
: this(a, offset, disable, _ => zOffset) { }
: this(a, offset, disable, () => false, _ => zOffset) { }
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, Func<WPos, int> zOffset)
public AnimationWithOffset(Animation a, Func<WVec> offset, Func<bool> disable, Func<bool> pause, Func<WPos, int> zOffset)
{
Animation = a;
OffsetFunc = offset;
DisableFunc = disable;
ZOffset = zOffset;
this.Animation = a;
this.Animation.Paused = pause;
this.OffsetFunc = offset;
this.DisableFunc = disable;
this.ZOffset = zOffset;
}
public IEnumerable<IRenderable> Render(Actor self, WorldRenderer wr, PaletteReference pal, float scale)
@@ -46,7 +47,7 @@ namespace OpenRA.Graphics
public static implicit operator AnimationWithOffset(Animation a)
{
return new AnimationWithOffset(a, null, null, null);
return new AnimationWithOffset(a, null, null, null, null);
}
}
}

View File

@@ -1,17 +1,15 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileSystem;
namespace OpenRA.Graphics
{
@@ -26,19 +24,16 @@ namespace OpenRA.Graphics
static Dictionary<string, Collection> collections;
static Dictionary<string, Sheet> cachedSheets;
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
static IReadOnlyFileSystem fileSystem;
public static void Initialize(ModData modData)
public static void Initialize(IEnumerable<string> chromeFiles)
{
Deinitialize();
fileSystem = modData.DefaultFileSystem;
collections = new Dictionary<string, Collection>();
cachedSheets = new Dictionary<string, Sheet>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
var chrome = MiniYaml.Merge(modData.Manifest.Chrome
.Select(s => MiniYaml.FromStream(fileSystem.Open(s))));
var chrome = chromeFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal);
foreach (var c in chrome)
LoadCollection(c.Key, c.Value);
@@ -88,9 +83,6 @@ namespace OpenRA.Graphics
public static Sprite GetImage(string collectionName, string imageName)
{
if (string.IsNullOrEmpty(collectionName))
return null;
// Cached sprite
Dictionary<string, Sprite> cachedCollection;
Sprite sprite;
@@ -114,9 +106,7 @@ namespace OpenRA.Graphics
sheet = cachedSheets[mi.Src];
else
{
using (var stream = fileSystem.Open(mi.Src))
sheet = new Sheet(SheetType.BGRA, stream);
sheet = new Sheet(SheetType.BGRA, mi.Src);
cachedSheets.Add(mi.Src, sheet);
}

View File

@@ -1,17 +1,19 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
@@ -22,13 +24,11 @@ namespace OpenRA.Graphics
public CursorProvider(ModData modData)
{
var fileSystem = modData.DefaultFileSystem;
var sequenceYaml = MiniYaml.Merge(modData.Manifest.Cursors.Select(
s => MiniYaml.FromStream(fileSystem.Open(s))));
var sequenceFiles = modData.Manifest.Cursors;
var sequences = new MiniYaml(null, sequenceFiles.Select(s => MiniYaml.FromFile(s)).Aggregate(MiniYaml.MergeLiberal));
var shadowIndex = new int[] { };
var nodesDict = new MiniYaml(null, sequenceYaml).ToDictionary();
var nodesDict = sequences.ToDictionary();
if (nodesDict.ContainsKey("ShadowIndex"))
{
Array.Resize(ref shadowIndex, shadowIndex.Length + 1);
@@ -38,11 +38,11 @@ namespace OpenRA.Graphics
var palettes = new Dictionary<string, ImmutablePalette>();
foreach (var p in nodesDict["Palettes"].Nodes)
palettes.Add(p.Key, new ImmutablePalette(fileSystem.Open(p.Value.Value), shadowIndex));
palettes.Add(p.Key, new ImmutablePalette(GlobalFileSystem.Open(p.Value.Value), shadowIndex));
Palettes = palettes.AsReadOnly();
var frameCache = new FrameCache(fileSystem, modData.SpriteLoaders);
var frameCache = new FrameCache(modData.SpriteLoaders);
var cursors = new Dictionary<string, CursorSequence>();
foreach (var s in nodesDict["Cursors"].Nodes)
foreach (var sequence in s.Value.Nodes)

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,16 +1,14 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Drawing;
using System.Globalization;
using OpenRA.Scripting;
namespace OpenRA.Graphics
@@ -29,17 +27,13 @@ namespace OpenRA.Graphics
return new HSLColor((byte)(255 * h), (byte)(255 * ss), (byte)(255 * ll));
}
public HSLColor(Color color)
{
RGB = color;
H = (byte)((color.GetHue() / 360.0f) * 255);
S = (byte)(color.GetSaturation() * 255);
L = (byte)(color.GetBrightness() * 255);
}
public static HSLColor FromRGB(int r, int g, int b)
{
return new HSLColor(Color.FromArgb(r, g, b));
var c = Color.FromArgb(r, g, b);
var h = (byte)((c.GetHue() / 360.0f) * 255);
var s = (byte)(c.GetSaturation() * 255);
var l = (byte)(c.GetBrightness() * 255);
return new HSLColor(h, s, l);
}
public static Color RGBFromHSL(float h, float s, float l)
@@ -72,27 +66,6 @@ namespace OpenRA.Graphics
return Color.FromArgb((int)(rgb[0] * 255), (int)(rgb[1] * 255), (int)(rgb[2] * 255));
}
public static bool TryParseRGB(string value, out Color color)
{
color = new Color();
value = value.Trim();
if (value.Length != 6 && value.Length != 8)
return false;
byte red, green, blue, alpha = 255;
if (!byte.TryParse(value.Substring(0, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out red)
|| !byte.TryParse(value.Substring(2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out green)
|| !byte.TryParse(value.Substring(4, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out blue))
return false;
if (value.Length == 8
&& !byte.TryParse(value.Substring(6, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out alpha))
return false;
color = Color.FromArgb(alpha, red, green, blue);
return true;
}
public static bool operator ==(HSLColor me, HSLColor other)
{
return me.H == other.H && me.S == other.S && me.L == other.L;
@@ -123,18 +96,6 @@ namespace OpenRA.Graphics
return "{0},{1},{2}".F(H, S, L);
}
public static string ToHexString(Color color)
{
if (color.A == 255)
return color.R.ToString("X2") + color.G.ToString("X2") + color.B.ToString("X2");
return color.R.ToString("X2") + color.G.ToString("X2") + color.B.ToString("X2") + color.A.ToString("X2");
}
public string ToHexString()
{
return ToHexString(RGB);
}
public override int GetHashCode() { return H.GetHashCode() ^ S.GetHashCode() ^ L.GetHashCode(); }
public override bool Equals(object obj)

View File

@@ -12,10 +12,12 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Graphics
{
public sealed class HardwareCursor : ICursor
public class HardwareCursor : ICursor
{
readonly Dictionary<string, IHardwareCursor[]> hardwareCursors = new Dictionary<string, IHardwareCursor[]>();
readonly CursorProvider cursorProvider;

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -62,9 +61,9 @@ namespace OpenRA
Bitmap TakeScreenshot();
void PumpInput(IInputHandler inputHandler);
string GetClipboardText();
bool SetClipboardText(string text);
void DrawPrimitives(PrimitiveType type, int firstVertex, int numVertices);
void SetLineWidth(float width);
void EnableScissor(int left, int top, int width, int height);
void DisableScissor();
@@ -78,8 +77,6 @@ namespace OpenRA
IHardwareCursor CreateHardwareCursor(string name, Size size, byte[] data, int2 hotspot);
void SetHardwareCursor(IHardwareCursor cursor);
string GLVersion { get; }
}
public interface IVertexBuffer<T> : IDisposable
@@ -125,6 +122,7 @@ namespace OpenRA
PointList,
LineList,
TriangleList,
QuadList,
}
public struct Range<T>

View File

@@ -0,0 +1,174 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
namespace OpenRA.Graphics
{
public class LineRenderer : Renderer.IBatchRenderer
{
static readonly float2 Offset = new float2(0.5f, 0.5f);
readonly Renderer renderer;
readonly IShader shader;
readonly Action renderAction;
readonly Vertex[] vertices;
int nv = 0;
float lineWidth = 1f;
public LineRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
renderAction = () =>
{
renderer.SetLineWidth(LineWidth);
renderer.DrawBatch(vertices, nv, PrimitiveType.LineList);
};
}
public float LineWidth
{
get
{
return lineWidth;
}
set
{
if (LineWidth != value)
Flush();
lineWidth = value;
}
}
public void Flush()
{
if (nv > 0)
{
renderer.Device.SetBlendMode(BlendMode.Alpha);
shader.Render(renderAction);
renderer.Device.SetBlendMode(BlendMode.None);
nv = 0;
}
}
public void DrawRect(float2 tl, float2 br, Color c)
{
var tr = new float2(br.X, tl.Y);
var bl = new float2(tl.X, br.Y);
DrawLine(tl, tr, c);
DrawLine(tl, bl, c);
DrawLine(tr, br, c);
DrawLine(bl, br, c);
}
public void DrawLine(float2 start, float2 end, Color color)
{
renderer.CurrentBatchRenderer = this;
if (nv + 2 > renderer.TempBufferSize)
Flush();
color = Util.PremultiplyAlpha(color);
var r = color.R / 255.0f;
var g = color.G / 255.0f;
var b = color.B / 255.0f;
var a = color.A / 255.0f;
vertices[nv++] = new Vertex(start + Offset, r, g, b, a);
vertices[nv++] = new Vertex(end + Offset, r, g, b, a);
}
public void DrawLine(float2 start, float2 end, Color startColor, Color endColor)
{
renderer.CurrentBatchRenderer = this;
if (nv + 2 > renderer.TempBufferSize)
Flush();
startColor = Util.PremultiplyAlpha(startColor);
var r = startColor.R / 255.0f;
var g = startColor.G / 255.0f;
var b = startColor.B / 255.0f;
var a = startColor.A / 255.0f;
vertices[nv++] = new Vertex(start + Offset, r, g, b, a);
endColor = Util.PremultiplyAlpha(endColor);
r = endColor.R / 255.0f;
g = endColor.G / 255.0f;
b = endColor.B / 255.0f;
a = endColor.A / 255.0f;
vertices[nv++] = new Vertex(end + Offset, r, g, b, a);
}
public void DrawLineStrip(IEnumerable<float2> points, Color color)
{
renderer.CurrentBatchRenderer = this;
color = Util.PremultiplyAlpha(color);
var r = color.R / 255.0f;
var g = color.G / 255.0f;
var b = color.B / 255.0f;
var a = color.A / 255.0f;
var first = true;
var prev = new Vertex();
foreach (var point in points)
{
if (first)
{
first = false;
prev = new Vertex(point + Offset, r, g, b, a);
continue;
}
if (nv + 2 > renderer.TempBufferSize)
Flush();
vertices[nv++] = prev;
prev = new Vertex(point + Offset, r, g, b, a);
vertices[nv++] = prev;
}
}
public void FillRect(RectangleF r, Color color)
{
for (var y = r.Top; y < r.Bottom; y++)
DrawLine(new float2(r.Left, y), new float2(r.Right, y), color);
}
public void FillEllipse(RectangleF r, Color color)
{
var a = (r.Right - r.Left) / 2;
var b = (r.Bottom - r.Top) / 2;
var xc = (r.Right + r.Left) / 2;
var yc = (r.Bottom + r.Top) / 2;
for (var y = r.Top; y <= r.Bottom; y++)
{
var dx = a * (float)Math.Sqrt(1 - (y - yc) * (y - yc) / b / b);
DrawLine(new float2(xc - dx, y), new float2(xc + dx, y), color);
}
}
public void SetViewportParams(Size screen, float zoom, int2 scroll)
{
shader.SetVec("Scroll", scroll.X, scroll.Y);
shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height);
shader.SetVec("r2", -1, 1);
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -38,7 +37,7 @@ namespace OpenRA.Graphics
if (defaultSrc != Src)
root.Add(new MiniYamlNode("Src", Src));
return new MiniYaml(FieldSaver.FormatValue(this, GetType().GetField("rect")), root);
return new MiniYaml(FieldSaver.FormatValue(this, this.GetType().GetField("rect")), root);
}
}
}

View File

@@ -0,0 +1,148 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
public static class Minimap
{
public static Bitmap TerrainBitmap(TileSet tileset, Map map, bool actualSize = false)
{
var isRectangularIsometric = map.Grid.Type == MapGridType.RectangularIsometric;
var b = map.Bounds;
// Fudge the heightmap offset by adding as much extra as we need / can.
// This tries to correct for our incorrect assumption that MPos == PPos
var heightOffset = Math.Min(map.Grid.MaximumTerrainHeight, map.MapSize.Y - b.Bottom);
var width = b.Width;
var height = b.Height + heightOffset;
var bitmapWidth = width;
if (isRectangularIsometric)
bitmapWidth = 2 * bitmapWidth - 1;
if (!actualSize)
bitmapWidth = height = Exts.NextPowerOf2(Math.Max(bitmapWidth, height));
var terrain = new Bitmap(bitmapWidth, height);
var bitmapData = terrain.LockBits(terrain.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
var mapTiles = map.MapTiles.Value;
unsafe
{
var colors = (int*)bitmapData.Scan0;
var stride = bitmapData.Stride / 4;
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var uv = new MPos(x + b.Left, y + b.Top);
var type = tileset.GetTileInfo(mapTiles[uv]);
var leftColor = type != null ? type.LeftColor : Color.Black;
if (isRectangularIsometric)
{
// Odd rows are shifted right by 1px
var dx = uv.V & 1;
var rightColor = type != null ? type.RightColor : Color.Black;
if (x + dx > 0)
colors[y * stride + 2 * x + dx - 1] = leftColor.ToArgb();
if (2 * x + dx < stride)
colors[y * stride + 2 * x + dx] = rightColor.ToArgb();
}
else
colors[y * stride + x] = leftColor.ToArgb();
}
}
}
terrain.UnlockBits(bitmapData);
return terrain;
}
// Add the static resources defined in the map; if the map lives
// in a world use AddCustomTerrain instead
static Bitmap AddStaticResources(TileSet tileset, Map map, Ruleset resourceRules, Bitmap terrainBitmap)
{
var terrain = new Bitmap(terrainBitmap);
var isRectangularIsometric = map.Grid.Type == MapGridType.RectangularIsometric;
var b = map.Bounds;
// Fudge the heightmap offset by adding as much extra as we need / can
// This tries to correct for our incorrect assumption that MPos == PPos
var heightOffset = Math.Min(map.Grid.MaximumTerrainHeight, map.MapSize.Y - b.Bottom);
var width = b.Width;
var height = b.Height + heightOffset;
var resources = resourceRules.Actors["world"].TraitInfos<ResourceTypeInfo>()
.ToDictionary(r => r.ResourceType, r => r.TerrainType);
var bitmapData = terrain.LockBits(terrain.Bounds(),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
var colors = (int*)bitmapData.Scan0;
var stride = bitmapData.Stride / 4;
for (var y = 0; y < height; y++)
{
for (var x = 0; x < width; x++)
{
var uv = new MPos(x + b.Left, y + b.Top);
if (map.MapResources.Value[uv].Type == 0)
continue;
string res;
if (!resources.TryGetValue(map.MapResources.Value[uv].Type, out res))
continue;
var color = tileset[tileset.GetTerrainIndex(res)].Color.ToArgb();
if (isRectangularIsometric)
{
// Odd rows are shifted right by 1px
var dx = uv.V & 1;
if (x + dx > 0)
colors[y * stride + 2 * x + dx - 1] = color;
if (2 * x + dx < stride)
colors[y * stride + 2 * x + dx] = color;
}
else
colors[y * stride + x] = color;
}
}
}
terrain.UnlockBits(bitmapData);
return terrain;
}
public static Bitmap RenderMapPreview(TileSet tileset, Map map, bool actualSize)
{
return RenderMapPreview(tileset, map, map.Rules, actualSize);
}
public static Bitmap RenderMapPreview(TileSet tileset, Map map, Ruleset resourceRules, bool actualSize)
{
using (var terrain = TerrainBitmap(tileset, map, actualSize))
return AddStaticResources(tileset, map, resourceRules, terrain);
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -58,7 +57,7 @@ namespace OpenRA.Graphics
var b = new Bitmap(Size, 1, PixelFormat.Format32bppArgb);
var data = b.LockBits(new Rectangle(0, 0, b.Width, b.Height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
var temp = new uint[Size];
var temp = new uint[Palette.Size];
palette.CopyToArray(temp, 0);
Marshal.Copy((int[])(object)temp, 0, data.Scan0, Size);
b.UnlockBits(data);

View File

@@ -1,14 +1,19 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
public sealed class PaletteReference

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -30,7 +29,7 @@ namespace OpenRA.Graphics
{
// Increase luminosity if required to represent the full ramp
var rampRange = (byte)((1 - rampFraction) * c.L);
var c1 = new HSLColor(c.H, c.S, Math.Max(rampRange, c.L)).RGB;
var c1 = new HSLColor(c.H, c.S, (byte)Math.Max(rampRange, c.L)).RGB;
var c2 = new HSLColor(c.H, c.S, (byte)Math.Max(0, c.L - rampRange)).RGB;
var baseIndex = ramp[0];
var remapRamp = ramp.Select(r => r - ramp[0]);

View File

@@ -0,0 +1,72 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Drawing;
namespace OpenRA.Graphics
{
public class QuadRenderer : Renderer.IBatchRenderer
{
readonly Renderer renderer;
readonly IShader shader;
readonly Action renderAction;
readonly Vertex[] vertices;
int nv = 0;
public QuadRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
renderAction = () => renderer.DrawBatch(vertices, nv, PrimitiveType.QuadList);
}
public void Flush()
{
if (nv > 0)
{
renderer.Device.SetBlendMode(BlendMode.Alpha);
shader.Render(renderAction);
renderer.Device.SetBlendMode(BlendMode.None);
nv = 0;
}
}
public void FillRect(RectangleF rect, Color color)
{
renderer.CurrentBatchRenderer = this;
if (nv + 4 > renderer.TempBufferSize)
Flush();
color = Util.PremultiplyAlpha(color);
var r = color.R / 255.0f;
var g = color.G / 255.0f;
var b = color.B / 255.0f;
var a = color.A / 255.0f;
vertices[nv] = new Vertex(new float2(rect.Left, rect.Top), r, g, b, a);
vertices[nv + 1] = new Vertex(new float2(rect.Right, rect.Top), r, g, b, a);
vertices[nv + 2] = new Vertex(new float2(rect.Right, rect.Bottom), r, g, b, a);
vertices[nv + 3] = new Vertex(new float2(rect.Left, rect.Bottom), r, g, b, a);
nv += 4;
}
public void SetViewportParams(Size screen, float zoom, int2 scroll)
{
shader.SetVec("Scroll", scroll.X, scroll.Y);
shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height);
shader.SetVec("r2", -1, 1);
}
}
}

View File

@@ -1,15 +1,16 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{

View File

@@ -1,275 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{
public class RgbaColorRenderer : Renderer.IBatchRenderer
{
static readonly float2 Offset = new float2(0.5f, 0.5f);
readonly Renderer renderer;
readonly IShader shader;
readonly Action renderAction;
readonly Vertex[] vertices;
int nv = 0;
public RgbaColorRenderer(Renderer renderer, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
renderAction = () => renderer.DrawBatch(vertices, nv, PrimitiveType.TriangleList);
}
public void Flush()
{
if (nv > 0)
{
renderer.Device.SetBlendMode(BlendMode.Alpha);
shader.Render(renderAction);
renderer.Device.SetBlendMode(BlendMode.None);
nv = 0;
}
}
public void DrawLine(float2 start, float2 end, float width, Color startColor, Color endColor)
{
renderer.CurrentBatchRenderer = this;
if (nv + 6 > renderer.TempBufferSize)
Flush();
var delta = (end - start) / (end - start).Length;
var corner = width / 2 * new float2(-delta.Y, delta.X);
startColor = Util.PremultiplyAlpha(startColor);
var sr = startColor.R / 255.0f;
var sg = startColor.G / 255.0f;
var sb = startColor.B / 255.0f;
var sa = startColor.A / 255.0f;
endColor = Util.PremultiplyAlpha(endColor);
var er = endColor.R / 255.0f;
var eg = endColor.G / 255.0f;
var eb = endColor.B / 255.0f;
var ea = endColor.A / 255.0f;
vertices[nv++] = new Vertex(start - corner + Offset, sr, sg, sb, sa);
vertices[nv++] = new Vertex(start + corner + Offset, sr, sg, sb, sa);
vertices[nv++] = new Vertex(end + corner + Offset, er, eg, eb, ea);
vertices[nv++] = new Vertex(end + corner + Offset, er, eg, eb, ea);
vertices[nv++] = new Vertex(end - corner + Offset, er, eg, eb, ea);
vertices[nv++] = new Vertex(start - corner + Offset, sr, sg, sb, sa);
}
public void DrawLine(float2 start, float2 end, float width, Color color)
{
renderer.CurrentBatchRenderer = this;
if (nv + 6 > renderer.TempBufferSize)
Flush();
var delta = (end - start) / (end - start).Length;
var corner = width / 2 * new float2(-delta.Y, delta.X);
color = Util.PremultiplyAlpha(color);
var r = color.R / 255.0f;
var g = color.G / 255.0f;
var b = color.B / 255.0f;
var a = color.A / 255.0f;
vertices[nv++] = new Vertex(start - corner + Offset, r, g, b, a);
vertices[nv++] = new Vertex(start + corner + Offset, r, g, b, a);
vertices[nv++] = new Vertex(end + corner + Offset, r, g, b, a);
vertices[nv++] = new Vertex(end + corner + Offset, r, g, b, a);
vertices[nv++] = new Vertex(end - corner + Offset, r, g, b, a);
vertices[nv++] = new Vertex(start - corner + Offset, r, g, b, a);
}
/// <summary>
/// Calculate the intersection of two lines.
/// Will behave badly if the lines are parallel
/// </summary>
float2 IntersectionOf(float2 a, float2 da, float2 b, float2 db)
{
var crossA = a.X * (a.Y + da.Y) - a.Y * (a.X + da.X);
var crossB = b.X * (b.Y + db.Y) - b.Y * (b.X + db.X);
var x = da.X * crossB - db.X * crossA;
var y = da.Y * crossB - db.Y * crossA;
var d = da.X * db.Y - da.Y * db.X;
return new float2(x, y) / d;
}
void DrawDisconnectedLine(IEnumerable<float2> points, float width, Color color)
{
using (var e = points.GetEnumerator())
{
if (!e.MoveNext())
return;
var lastPoint = e.Current;
while (e.MoveNext())
{
var point = e.Current;
DrawLine(lastPoint, point, width, color);
lastPoint = point;
}
}
}
void DrawConnectedLine(float2[] points, float width, Color color, bool closed)
{
// Not a line
if (points.Length < 2)
return;
// Single segment
if (points.Length == 2)
{
DrawLine(points[0], points[1], width, color);
return;
}
renderer.CurrentBatchRenderer = this;
color = Util.PremultiplyAlpha(color);
var r = color.R / 255.0f;
var g = color.G / 255.0f;
var b = color.B / 255.0f;
var a = color.A / 255.0f;
var start = points[0];
var end = points[1];
var dir = (end - start) / (end - start).Length;
var corner = width / 2 * new float2(-dir.Y, dir.X);
// Corners for start of line segment
var ca = start - corner;
var cb = start + corner;
// Segment is part of closed loop
if (closed)
{
var prev = points[points.Length - 1];
var prevDir = (start - prev) / (start - prev).Length;
var prevCorner = width / 2 * new float2(-prevDir.Y, prevDir.X);
ca = IntersectionOf(start - prevCorner, prevDir, start - corner, dir);
cb = IntersectionOf(start + prevCorner, prevDir, start + corner, dir);
}
var limit = closed ? points.Length : points.Length - 1;
for (var i = 0; i < limit; i++)
{
var next = points[(i + 2) % points.Length];
var nextDir = (next - end) / (next - end).Length;
var nextCorner = width / 2 * new float2(-nextDir.Y, nextDir.X);
// Vertices for the corners joining start-end to end-next
var cc = closed || i < limit ? IntersectionOf(end + corner, dir, end + nextCorner, nextDir) : end + corner;
var cd = closed || i < limit ? IntersectionOf(end - corner, dir, end - nextCorner, nextDir) : end - corner;
// Fill segment
if (nv + 6 > renderer.TempBufferSize)
Flush();
vertices[nv++] = new Vertex(ca + Offset, r, g, b, a);
vertices[nv++] = new Vertex(cb + Offset, r, g, b, a);
vertices[nv++] = new Vertex(cc + Offset, r, g, b, a);
vertices[nv++] = new Vertex(cc + Offset, r, g, b, a);
vertices[nv++] = new Vertex(cd + Offset, r, g, b, a);
vertices[nv++] = new Vertex(ca + Offset, r, g, b, a);
// Advance line segment
end = next;
dir = nextDir;
corner = nextCorner;
ca = cd;
cb = cc;
}
}
public void DrawLine(IEnumerable<float2> points, float width, Color color, bool connectSegments = false)
{
if (!connectSegments)
DrawDisconnectedLine(points, width, color);
else
DrawConnectedLine(points as float2[] ?? points.ToArray(), width, color, false);
}
public void DrawPolygon(float2[] vertices, float width, Color color)
{
DrawConnectedLine(vertices, width, color, true);
}
public void DrawRect(float2 tl, float2 br, float width, Color color)
{
var tr = new float2(br.X, tl.Y);
var bl = new float2(tl.X, br.Y);
DrawPolygon(new[] { tl, tr, br, bl }, width, color);
}
public void FillRect(float2 tl, float2 br, Color color)
{
var tr = new float2(br.X, tl.Y);
var bl = new float2(tl.X, br.Y);
FillRect(tl, tr, br, bl, color);
}
public void FillRect(float2 a, float2 b, float2 c, float2 d, Color color)
{
renderer.CurrentBatchRenderer = this;
if (nv + 6 > renderer.TempBufferSize)
Flush();
color = Util.PremultiplyAlpha(color);
var cr = color.R / 255.0f;
var cg = color.G / 255.0f;
var cb = color.B / 255.0f;
var ca = color.A / 255.0f;
vertices[nv++] = new Vertex(a + Offset, cr, cg, cb, ca);
vertices[nv++] = new Vertex(b + Offset, cr, cg, cb, ca);
vertices[nv++] = new Vertex(c + Offset, cr, cg, cb, ca);
vertices[nv++] = new Vertex(c + Offset, cr, cg, cb, ca);
vertices[nv++] = new Vertex(d + Offset, cr, cg, cb, ca);
vertices[nv++] = new Vertex(a + Offset, cr, cg, cb, ca);
}
public void FillEllipse(RectangleF r, Color color, int vertices = 32)
{
// TODO: Create an ellipse polygon instead
var a = (r.Right - r.Left) / 2;
var b = (r.Bottom - r.Top) / 2;
var xc = (r.Right + r.Left) / 2;
var yc = (r.Bottom + r.Top) / 2;
for (var y = r.Top; y <= r.Bottom; y++)
{
var dx = a * (float)Math.Sqrt(1 - (y - yc) * (y - yc) / b / b);
DrawLine(new float2(xc - dx, y), new float2(xc + dx, y), 1, color);
}
}
public void SetViewportParams(Size screen, float zoom, int2 scroll)
{
shader.SetVec("Scroll", scroll.X, scroll.Y);
shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height);
shader.SetVec("r2", -1, 1);
}
}
}

View File

@@ -1,15 +1,15 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Drawing;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Graphics
@@ -18,26 +18,17 @@ namespace OpenRA.Graphics
{
readonly WPos pos;
readonly Actor actor;
readonly bool displayHealth;
readonly bool displayExtra;
public SelectionBarsRenderable(Actor actor, bool displayHealth, bool displayExtra)
: this(actor.CenterPosition, actor)
{
this.displayHealth = displayHealth;
this.displayExtra = displayExtra;
}
public SelectionBarsRenderable(Actor actor)
: this(actor.CenterPosition, actor) { }
public SelectionBarsRenderable(WPos pos, Actor actor)
: this()
{
this.pos = pos;
this.actor = actor;
}
public WPos Pos { get { return pos; } }
public bool DisplayHealth { get { return displayHealth; } }
public bool DisplayExtra { get { return displayExtra; } }
public PaletteReference Palette { get { return null; } }
public int ZOffset { get { return 0; } }
@@ -64,30 +55,50 @@ namespace OpenRA.Graphics
void DrawSelectionBar(WorldRenderer wr, float2 start, float2 end, float value, Color barColor)
{
var iz = 1 / wr.Viewport.Zoom;
var c = Color.FromArgb(128, 30, 30, 30);
var c2 = Color.FromArgb(128, 10, 10, 10);
var p = new float2(0, -4 * iz);
var q = new float2(0, -3 * iz);
var r = new float2(0, -2 * iz);
var p = new float2(0, -4 / wr.Viewport.Zoom);
var q = new float2(0, -3 / wr.Viewport.Zoom);
var r = new float2(0, -2 / wr.Viewport.Zoom);
var barColor2 = Color.FromArgb(255, barColor.R / 2, barColor.G / 2, barColor.B / 2);
var z = float2.Lerp(start, end, value);
var wcr = Game.Renderer.WorldRgbaColorRenderer;
wcr.DrawLine(start + p, end + p, iz, c);
wcr.DrawLine(start + q, end + q, iz, c2);
wcr.DrawLine(start + r, end + r, iz, c);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(start + p, end + p, c);
wlr.DrawLine(start + q, end + q, c2);
wlr.DrawLine(start + r, end + r, c);
wcr.DrawLine(start + p, z + p, iz, barColor2);
wcr.DrawLine(start + q, z + q, iz, barColor);
wcr.DrawLine(start + r, z + r, iz, barColor2);
wlr.DrawLine(start + p, z + p, barColor2);
wlr.DrawLine(start + q, z + q, barColor);
wlr.DrawLine(start + r, z + r, barColor2);
}
Color GetHealthColor(IHealth health)
{
if (Game.Settings.Game.UsePlayerStanceColors)
return actor.Owner.PlayerStanceColor(actor);
var player = actor.World.RenderPlayer ?? actor.World.LocalPlayer;
if (Game.Settings.Game.TeamHealthColors && player != null && !player.Spectating)
{
var apparentOwner = actor.EffectiveOwner != null && actor.EffectiveOwner.Disguised
? actor.EffectiveOwner.Owner
: actor.Owner;
// For friendly spies, treat the unit's owner as the actual owner
if (actor.Owner.IsAlliedWith(actor.World.RenderPlayer))
apparentOwner = actor.Owner;
if (apparentOwner == player)
return Color.LimeGreen;
if (apparentOwner.IsAlliedWith(player))
return Color.Yellow;
if (apparentOwner.NonCombatant)
return Color.Tan;
return Color.Red;
}
else
return health.DamageState == DamageState.Critical ? Color.Red :
health.DamageState == DamageState.Heavy ? Color.Yellow : Color.LimeGreen;
@@ -100,10 +111,9 @@ namespace OpenRA.Graphics
var c = Color.FromArgb(128, 30, 30, 30);
var c2 = Color.FromArgb(128, 10, 10, 10);
var iz = 1 / wr.Viewport.Zoom;
var p = new float2(0, -4 * iz);
var q = new float2(0, -3 * iz);
var r = new float2(0, -2 * iz);
var p = new float2(0, -4 / wr.Viewport.Zoom);
var q = new float2(0, -3 / wr.Viewport.Zoom);
var r = new float2(0, -2 / wr.Viewport.Zoom);
var healthColor = GetHealthColor(health);
var healthColor2 = Color.FromArgb(
@@ -114,14 +124,14 @@ namespace OpenRA.Graphics
var z = float2.Lerp(start, end, (float)health.HP / health.MaxHP);
var wcr = Game.Renderer.WorldRgbaColorRenderer;
wcr.DrawLine(start + p, end + p, iz, c);
wcr.DrawLine(start + q, end + q, iz, c2);
wcr.DrawLine(start + r, end + r, iz, c);
var wlr = Game.Renderer.WorldLineRenderer;
wlr.DrawLine(start + p, end + p, c);
wlr.DrawLine(start + q, end + q, c2);
wlr.DrawLine(start + r, end + r, c);
wcr.DrawLine(start + p, z + p, iz, healthColor2);
wcr.DrawLine(start + q, z + q, iz, healthColor);
wcr.DrawLine(start + r, z + r, iz, healthColor2);
wlr.DrawLine(start + p, z + p, healthColor2);
wlr.DrawLine(start + q, z + q, healthColor);
wlr.DrawLine(start + r, z + r, healthColor2);
if (health.DisplayHP != health.HP)
{
@@ -133,9 +143,9 @@ namespace OpenRA.Graphics
deltaColor.B / 2);
var zz = float2.Lerp(start, end, (float)health.DisplayHP / health.MaxHP);
wcr.DrawLine(z + p, zz + p, iz, deltaColor2);
wcr.DrawLine(z + q, zz + q, iz, deltaColor);
wcr.DrawLine(z + r, zz + r, iz, deltaColor2);
wlr.DrawLine(z + p, zz + p, deltaColor2);
wlr.DrawLine(z + q, zz + q, deltaColor);
wlr.DrawLine(z + r, zz + r, deltaColor2);
}
}
@@ -154,11 +164,8 @@ namespace OpenRA.Graphics
var start = new float2(bounds.Left + 1, bounds.Top);
var end = new float2(bounds.Right - 1, bounds.Top);
if (DisplayHealth)
DrawHealthBar(wr, health, start, end);
if (DisplayExtra)
DrawExtraBars(wr, start, end);
DrawHealthBar(wr, health, start, end);
DrawExtraBars(wr, start, end);
}
public void RenderDebugGeometry(WorldRenderer wr) { }

View File

@@ -1,18 +1,18 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OpenRA.FileSystem;
using System.Reflection;
namespace OpenRA.Graphics
{
@@ -43,27 +43,15 @@ namespace OpenRA.Graphics
IReadOnlyDictionary<string, ISpriteSequence> ParseSequences(ModData modData, TileSet tileSet, SpriteCache cache, MiniYamlNode node);
}
public class SequenceProvider : IDisposable
public class SequenceProvider
{
readonly ModData modData;
readonly TileSet tileSet;
readonly Lazy<Sequences> sequences;
readonly Lazy<SpriteCache> spriteCache;
public SpriteCache SpriteCache { get { return spriteCache.Value; } }
public readonly SpriteCache SpriteCache;
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
public SequenceProvider(IReadOnlyFileSystem fileSystem, ModData modData, TileSet tileSet, MiniYaml additionalSequences)
public SequenceProvider(SequenceCache cache, Map map)
{
this.modData = modData;
this.tileSet = tileSet;
sequences = Exts.Lazy(() =>
{
using (new Support.PerfTimer("LoadSequences"))
return Load(fileSystem, additionalSequences);
});
spriteCache = Exts.Lazy(() => new SpriteCache(fileSystem, modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed)));
this.sequences = Exts.Lazy(() => cache.LoadSequences(map));
this.SpriteCache = cache.SpriteCache;
}
public ISpriteSequence GetSequence(string unitName, string sequenceName)
@@ -102,9 +90,47 @@ namespace OpenRA.Graphics
return unitSeq.Value.Keys;
}
Sequences Load(IReadOnlyFileSystem fileSystem, MiniYaml additionalSequences)
public void Preload()
{
var nodes = MiniYaml.Load(fileSystem, modData.Manifest.Sequences, additionalSequences);
SpriteCache.SheetBuilder.Current.CreateBuffer();
foreach (var unitSeq in sequences.Value.Values)
foreach (var seq in unitSeq.Value.Values) { }
SpriteCache.SheetBuilder.Current.ReleaseBuffer();
}
}
public sealed class SequenceCache : IDisposable
{
readonly ModData modData;
readonly TileSet tileSet;
readonly Lazy<SpriteCache> spriteCache;
public SpriteCache SpriteCache { get { return spriteCache.Value; } }
readonly Dictionary<string, UnitSequences> sequenceCache = new Dictionary<string, UnitSequences>();
public SequenceCache(ModData modData, TileSet tileSet)
{
this.modData = modData;
this.tileSet = tileSet;
// Every time we load a tile set, we create a sequence cache for it
spriteCache = Exts.Lazy(() => new SpriteCache(modData.SpriteLoaders, new SheetBuilder(SheetType.Indexed)));
}
public Sequences LoadSequences(Map map)
{
using (new Support.PerfTimer("LoadSequences"))
return Load(map != null ? map.SequenceDefinitions : new List<MiniYamlNode>());
}
Sequences Load(List<MiniYamlNode> sequenceNodes)
{
var sequenceFiles = modData.Manifest.Sequences;
var nodes = sequenceFiles
.Select(s => MiniYaml.FromFile(s))
.Aggregate(sequenceNodes, MiniYaml.MergeLiberal);
var items = new Dictionary<string, UnitSequences>();
foreach (var n in nodes)
{
@@ -127,14 +153,6 @@ namespace OpenRA.Graphics
return new ReadOnlyDictionary<string, UnitSequences>(items);
}
public void Preload()
{
SpriteCache.SheetBuilder.Current.CreateBuffer();
foreach (var unitSeq in sequences.Value.Values)
foreach (var seq in unitSeq.Value.Values) { }
SpriteCache.SheetBuilder.Current.ReleaseBuffer();
}
public void Dispose()
{
if (spriteCache.IsValueCreated)

View File

@@ -1,19 +1,18 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Runtime.InteropServices;
using OpenRA.FileSystem;
namespace OpenRA.Graphics
{
@@ -48,8 +47,9 @@ namespace OpenRA.Graphics
Size = texture.Size;
}
public Sheet(SheetType type, Stream stream)
public Sheet(SheetType type, string filename)
{
using (var stream = GlobalFileSystem.Open(filename))
using (var bitmap = (Bitmap)Image.FromStream(stream))
{
Size = bitmap.Size;

View File

@@ -1,17 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{

View File

@@ -10,7 +10,9 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Graphics
@@ -22,7 +24,7 @@ namespace OpenRA.Graphics
void Tick();
}
public sealed class SoftwareCursor : ICursor
public class SoftwareCursor : ICursor
{
readonly HardwarePalette palette = new HardwarePalette();
readonly Cache<string, PaletteReference> paletteReferences;

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -18,7 +17,7 @@ using SharpFont;
namespace OpenRA.Graphics
{
public sealed class SpriteFont : IDisposable
public class SpriteFont
{
static readonly Library Library = new Library();
@@ -28,7 +27,7 @@ namespace OpenRA.Graphics
readonly Face face;
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
public SpriteFont(string name, byte[] data, int size, SheetBuilder builder)
public SpriteFont(string name, int size, SheetBuilder builder)
{
if (builder.Type != SheetType.BGRA)
throw new ArgumentException("The sheet builder must create BGRA sheets.", "builder");
@@ -36,12 +35,11 @@ namespace OpenRA.Graphics
this.size = size;
this.builder = builder;
face = new Face(Library, data, 0);
face = new Face(Library, name);
face.SetPixelSizes((uint)size, (uint)size);
glyphs = new Cache<Pair<char, Color>, GlyphInfo>(CreateGlyph, Pair<char, Color>.EqualityComparer);
// PERF: Cache these delegates for Measure calls.
Func<char, float> characterWidth = character => glyphs[Pair.New(character, Color.White)].Advance;
lineWidth = line => line.Sum(characterWidth);
@@ -151,11 +149,6 @@ namespace OpenRA.Graphics
return g;
}
public void Dispose()
{
face.Dispose();
}
}
class GlyphInfo

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -24,17 +23,8 @@ namespace OpenRA.Graphics
public interface ISpriteFrame
{
/// <summary>
/// Size of the frame's `Data`.
/// </summary>
Size Size { get; }
/// <summary>
/// Size of the entire frame including the frame's `Size`.
/// Think of this like a picture frame.
/// </summary>
Size FrameSize { get; }
float2 Offset { get; }
byte[] Data { get; }
bool DisableExportPadding { get; }
@@ -45,11 +35,11 @@ namespace OpenRA.Graphics
public readonly SheetBuilder SheetBuilder;
readonly Cache<string, Sprite[]> sprites;
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
public SpriteCache(ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
{
SheetBuilder = sheetBuilder;
sprites = new Cache<string, Sprite[]>(filename => SpriteLoader.GetSprites(fileSystem, filename, loaders, sheetBuilder));
sprites = new Cache<string, Sprite[]>(filename => SpriteLoader.GetSprites(filename, loaders, sheetBuilder));
}
public Sprite[] this[string filename] { get { return sprites[filename]; } }
@@ -59,9 +49,9 @@ namespace OpenRA.Graphics
{
readonly Cache<string, ISpriteFrame[]> frames;
public FrameCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders)
public FrameCache(ISpriteLoader[] loaders)
{
frames = new Cache<string, ISpriteFrame[]>(filename => SpriteLoader.GetFrames(fileSystem, filename, loaders));
frames = new Cache<string, ISpriteFrame[]>(filename => SpriteLoader.GetFrames(filename, loaders));
}
public ISpriteFrame[] this[string filename] { get { return frames[filename]; } }
@@ -69,31 +59,22 @@ namespace OpenRA.Graphics
public static class SpriteLoader
{
public static Sprite[] GetSprites(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
public static Sprite[] GetSprites(string filename, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
{
return GetFrames(fileSystem, filename, loaders).Select(a => sheetBuilder.Add(a)).ToArray();
return GetFrames(filename, loaders).Select(a => sheetBuilder.Add(a)).ToArray();
}
public static ISpriteFrame[] GetFrames(IReadOnlyFileSystem fileSystem, string filename, ISpriteLoader[] loaders)
public static ISpriteFrame[] GetFrames(string filename, ISpriteLoader[] loaders)
{
using (var stream = fileSystem.Open(filename))
using (var stream = GlobalFileSystem.Open(filename))
{
var spriteFrames = GetFrames(stream, loaders);
if (spriteFrames == null)
throw new InvalidDataException(filename + " is not a valid sprite file!");
ISpriteFrame[] frames;
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, out frames))
return frames;
return spriteFrames;
throw new InvalidDataException(filename + " is not a valid sprite file");
}
}
public static ISpriteFrame[] GetFrames(Stream stream, ISpriteLoader[] loaders)
{
ISpriteFrame[] frames;
foreach (var loader in loaders)
if (loader.TryParseSprite(stream, out frames))
return frames;
return null;
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -63,7 +62,7 @@ namespace OpenRA.Graphics
public void RenderDebugGeometry(WorldRenderer wr)
{
var offset = ScreenPosition(wr) + sprite.Offset;
Game.Renderer.WorldRgbaColorRenderer.DrawRect(offset, offset + sprite.Size, 1 / wr.Viewport.Zoom, Color.Red);
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.Size, Color.Red);
}
public Rectangle ScreenBounds(WorldRenderer wr)

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -30,7 +29,7 @@ namespace OpenRA.Graphics
this.renderer = renderer;
this.shader = shader;
vertices = new Vertex[renderer.TempBufferSize];
renderAction = () => renderer.DrawBatch(vertices, nv, PrimitiveType.TriangleList);
renderAction = () => renderer.DrawBatch(vertices, nv, PrimitiveType.QuadList);
}
public void Flush()
@@ -52,7 +51,7 @@ namespace OpenRA.Graphics
{
renderer.CurrentBatchRenderer = this;
if (s.BlendMode != currentBlend || s.Sheet != currentSheet || nv + 6 > renderer.TempBufferSize)
if (s.BlendMode != currentBlend || s.Sheet != currentSheet || nv + 4 > renderer.TempBufferSize)
Flush();
currentBlend = s.BlendMode;
@@ -73,7 +72,7 @@ namespace OpenRA.Graphics
{
SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, paletteTextureIndex, nv, size);
nv += 6;
nv += 4;
}
// For RGBASpriteRenderer, which doesn't use palettes
@@ -91,14 +90,14 @@ namespace OpenRA.Graphics
{
SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, a, b, c, d, s, 0, nv);
nv += 6;
nv += 4;
}
public void DrawSprite(Sprite s, Vertex[] sourceVertices, int offset)
{
SetRenderStateForSprite(s);
Array.Copy(sourceVertices, offset, vertices, nv, 6);
nv += 6;
Array.Copy(sourceVertices, offset, vertices, nv, 4);
nv += 4;
}
public void DrawVertexBuffer(IVertexBuffer<Vertex> buffer, int start, int length, PrimitiveType type, Sheet sheet, BlendMode blendMode)

View File

@@ -1,17 +1,18 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
@@ -42,26 +43,16 @@ namespace OpenRA.Graphics
if (!waypoints.Any())
return;
var iz = 1 / wr.Viewport.Zoom;
var first = wr.ScreenPxPosition(waypoints.First());
var a = first;
foreach (var b in waypoints.Skip(1).Select(pos => wr.ScreenPxPosition(pos)))
{
Game.Renderer.WorldRgbaColorRenderer.DrawLine(a, b, iz, color);
DrawTargetMarker(wr, color, b);
Game.Renderer.WorldLineRenderer.DrawLine(a, b, color);
wr.DrawTargetMarker(color, b);
a = b;
}
DrawTargetMarker(wr, color, first);
}
public static void DrawTargetMarker(WorldRenderer wr, Color color, float2 location)
{
var iz = 1 / wr.Viewport.Zoom;
var offset = new float2(iz, iz);
var tl = location - offset;
var br = location + offset;
Game.Renderer.WorldRgbaColorRenderer.FillRect(tl, br, color);
wr.DrawTargetMarker(color, first);
}
public void RenderDebugGeometry(WorldRenderer wr) { }

View File

@@ -1,73 +1,58 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
sealed class TerrainRenderer : IDisposable
{
readonly Map map;
readonly Dictionary<string, TerrainSpriteLayer> spriteLayers = new Dictionary<string, TerrainSpriteLayer>();
readonly TerrainSpriteLayer terrain;
readonly Theater theater;
readonly CellLayer<TerrainTile> mapTiles;
readonly CellLayer<byte> mapHeight;
public TerrainRenderer(World world, WorldRenderer wr)
{
map = world.Map;
theater = wr.Theater;
mapTiles = world.Map.MapTiles.Value;
mapHeight = world.Map.MapHeight.Value;
foreach (var template in map.Rules.TileSet.Templates)
{
var palette = template.Value.Palette ?? TileSet.TerrainPaletteInternalName;
spriteLayers.GetOrAdd(palette, pal =>
new TerrainSpriteLayer(world, wr, theater.Sheet, BlendMode.Alpha, wr.Palette(palette), world.Type != WorldType.Editor));
}
terrain = new TerrainSpriteLayer(world, wr, theater.Sheet, BlendMode.Alpha,
wr.Palette("terrain"), wr.World.Type != WorldType.Editor);
foreach (var cell in map.AllCells)
foreach (var cell in world.Map.AllCells)
UpdateCell(cell);
map.Tiles.CellEntryChanged += UpdateCell;
map.Height.CellEntryChanged += UpdateCell;
mapTiles.CellEntryChanged += UpdateCell;
mapHeight.CellEntryChanged += UpdateCell;
}
public void UpdateCell(CPos cell)
{
var tile = map.Tiles[cell];
var palette = TileSet.TerrainPaletteInternalName;
if (map.Rules.TileSet.Templates.ContainsKey(tile.Type))
palette = map.Rules.TileSet.Templates[tile.Type].Palette ?? palette;
var sprite = theater.TileSprite(tile);
foreach (var kv in spriteLayers)
kv.Value.Update(cell, palette == kv.Key ? sprite : null);
terrain.Update(cell, theater.TileSprite(mapTiles[cell]));
}
public void Draw(WorldRenderer wr, Viewport viewport)
{
foreach (var kv in spriteLayers.Values)
kv.Draw(wr.Viewport);
terrain.Draw(viewport);
foreach (var r in wr.World.WorldActor.TraitsImplementing<IRenderOverlay>())
r.Render(wr);
}
public void Dispose()
{
map.Tiles.CellEntryChanged -= UpdateCell;
map.Height.CellEntryChanged -= UpdateCell;
foreach (var kv in spriteLayers.Values)
kv.Dispose();
mapTiles.CellEntryChanged -= UpdateCell;
mapHeight.CellEntryChanged -= UpdateCell;
terrain.Dispose();
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -13,6 +12,8 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
@@ -43,7 +44,7 @@ namespace OpenRA.Graphics
this.palette = palette;
map = world.Map;
rowStride = 6 * map.MapSize.X;
rowStride = 4 * map.MapSize.X;
vertices = new Vertex[rowStride * map.MapSize.Y];
vertexBuffer = Game.Renderer.Device.CreateVertexBuffer(vertices.Length);
@@ -86,7 +87,7 @@ namespace OpenRA.Graphics
else
sprite = emptySprite;
var offset = rowStride * uv.V + 6 * uv.U;
var offset = rowStride * uv.V + 4 * uv.U;
Util.FastCreateQuad(vertices, pos, sprite, palette.TextureIndex, offset, sprite.Size);
dirtyRows.Add(uv.V);
@@ -122,7 +123,7 @@ namespace OpenRA.Graphics
Game.Renderer.WorldSpriteRenderer.DrawVertexBuffer(
vertexBuffer, rowStride * firstRow, rowStride * (lastRow - firstRow),
PrimitiveType.TriangleList, Sheet, BlendMode);
PrimitiveType.QuadList, Sheet, BlendMode);
Game.Renderer.Flush();
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
@@ -13,6 +12,7 @@ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileSystem;
using OpenRA.Support;
namespace OpenRA.Graphics
@@ -57,7 +57,7 @@ namespace OpenRA.Graphics
sheetBuilder = new SheetBuilder(type, allocate);
random = new MersenneTwister();
var frameCache = new FrameCache(Game.ModData.DefaultFileSystem, Game.ModData.SpriteLoaders);
var frameCache = new FrameCache(Game.ModData.SpriteLoaders);
foreach (var t in tileset.Templates)
{
var variants = new List<Sprite[]>();

View File

@@ -1,31 +1,30 @@
#region Copyright & License Information
/*
* Copyright 2007-2016 The OpenRA Developers (see AUTHORS)
* Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
* as published by the Free Software Foundation. For more information,
* see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
namespace OpenRA.Graphics
{
public struct UISpriteRenderable : IRenderable, IFinalizedRenderable
{
readonly Sprite sprite;
readonly WPos effectiveWorldPos;
readonly int2 screenPos;
readonly int zOffset;
readonly PaletteReference palette;
readonly float scale;
public UISpriteRenderable(Sprite sprite, WPos effectiveWorldPos, int2 screenPos, int zOffset, PaletteReference palette, float scale)
public UISpriteRenderable(Sprite sprite, int2 screenPos, int zOffset, PaletteReference palette, float scale)
{
this.sprite = sprite;
this.effectiveWorldPos = effectiveWorldPos;
this.screenPos = screenPos;
this.zOffset = zOffset;
this.palette = palette;
@@ -33,14 +32,14 @@ namespace OpenRA.Graphics
}
// Does not exist in the world, so a world positions don't make sense
public WPos Pos { get { return effectiveWorldPos; } }
public WPos Pos { get { return WPos.Zero; } }
public WVec Offset { get { return WVec.Zero; } }
public bool IsDecoration { get { return true; } }
public PaletteReference Palette { get { return palette; } }
public int ZOffset { get { return zOffset; } }
public IRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, effectiveWorldPos, screenPos, zOffset, newPalette, scale); }
public IRenderable WithPalette(PaletteReference newPalette) { return new UISpriteRenderable(sprite, screenPos, zOffset, newPalette, scale); }
public IRenderable WithZOffset(int newOffset) { return this; }
public IRenderable OffsetBy(WVec vec) { return this; }
public IRenderable AsDecoration() { return this; }
@@ -54,7 +53,7 @@ namespace OpenRA.Graphics
public void RenderDebugGeometry(WorldRenderer wr)
{
var offset = screenPos + sprite.Offset;
Game.Renderer.RgbaColorRenderer.DrawRect(offset, offset + sprite.Size, 1, Color.Red);
Game.Renderer.LineRenderer.DrawRect(offset, offset + sprite.Size, Color.Red);
}
public Rectangle ScreenBounds(WorldRenderer wr)

Some files were not shown because too many files have changed in this diff Show More