Compare commits

..

145 Commits

Author SHA1 Message Date
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
2413 changed files with 66957 additions and 142424 deletions

10
.gitignore vendored
View File

@@ -21,7 +21,6 @@ _ReSharper.*/
# binaries
mods/*/*.dll
mods/*/*.mdb
mods/*/*.pdb
/*.dll
/*.dll.config
/*.so
@@ -71,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.6.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.5"
- make all
- make check
- make check-scripts
- make test
- make nunit
# Automatically update the trait documentation and Lua API
after_success:

29
AUTHORS
View File

@@ -8,6 +8,7 @@ The OpenRA developers are:
* Matthias Mailänder (Mailaender)
* Oliver Brakmann (obrakmann)
* Paul Chote (pchote)
* Pavel Penev (penev92)
* Reaperrr
* Tom Roostan (RoosterDragon)
@@ -18,7 +19,6 @@ Previous developers included:
* Daniel Hernandez (Mancano)
* Megan Bowra-Dean (beedee)
* Mike Bundy (kehaar)
* Pavel Penev (penev92)
* Robert Pepperell (ytinasni)
* ScottNZ
@@ -26,7 +26,6 @@ Also thanks to:
* Adam Valy (Tschokky)
* Akseli Virtanen (RAGEQUIT)
* Alexander Fast (mizipzor)
* Alexis Hunt (alercah)
* Allen262
* Andrew Aldridge (i80and)
* Andrew Perkins
@@ -56,10 +55,9 @@ Also thanks to:
* Eric Bajumpaa (SteelPhase)
* Evgeniy Sergeev (evgeniysergeev)
* Fahrradkette
* Florian Wiesbauer (FiveAces)
* Frank Razenberg (zzattack)
* Gareth Needham (Ripley`)
* Glen Anderson (GlenLife)
* Glen Anderson (GlenAnderson)
* Glenn Martin Jensen (Baxxster)
* Gordon Martin (Happy0)
* Guido Lipke (LipkeGu)
@@ -77,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
@@ -95,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,18 +100,13 @@ Also thanks to:
* Paolo Chiodi (paolochiodi)
* Paul Dovydaitis (pdovy)
* Pavlos Touboulidis (pav)
* Pedro Ferreira Ramos (bateramos)
* 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)
@@ -127,7 +116,6 @@ Also thanks to:
* Tirili
* Tristan Keating (Kilkakon)
* Tristan Mühlbacher (MicroBit)
* UnknownProgrammer
* Vladimir Komarov (VrKomarov)
* Wuschel
@@ -151,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.
@@ -160,9 +151,8 @@ under the MIT license.
Using FuzzyLogicLibrary (fuzzynet) by Dmitry
Kaluzhny and released under the GNU GPL terms.
Using Open.Nat by Lucas Ontivero, based on the work
of Alan McGovern and Ben Motmans and distributed
under the MIT license.
Using Mono.Nat by Alan McGovern and Ben
Motmans and distributed under the MIT license.
Using ICSharpCode.SharpZipLib initially by Mike
Krueger and distributed under the GNU GPL terms.
@@ -174,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

@@ -17,7 +17,7 @@
Please `git rebase` to the latest revision of the bleed branch.
Don't forget to add yourself to [AUTHORS](https://github.com/OpenRA/OpenRA/blob/bleed/AUTHORS).
Don't forget to add youself to [AUTHORS](https://github.com/OpenRA/OpenRA/blob/bleed/AUTHORS).
Please propose a [CHANGELOG](https://github.com/OpenRA/OpenRA/wiki/CHANGELOG) entry in the pull-request comments.

File diff suppressed because one or more lines are too long

View File

@@ -7,7 +7,7 @@ Windows
=======
* [Windows PowerShell >= 4.0](http://microsoft.com/powershell)
* [.NET Framework >= 4.5 (Client Profile)](http://www.microsoft.com/en-us/download/details.aspx?id=30653)
* [.NET Framework >= 4.0 (Client Profile)](http://www.microsoft.com/en-us/download/details.aspx?id=17113)
* [SDL 2](http://www.libsdl.org/download-2.0.php) (included)
* [FreeType](http://gnuwin32.sourceforge.net/packages/freetype.htm) (included)
* [zlib](http://gnuwin32.sourceforge.net/packages/zlib.htm) (included)
@@ -34,15 +34,16 @@ 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
* libsdl2-2.0-0
* xdg-utils
* zenity
* curl
openSUSE
--------
@@ -55,7 +56,6 @@ openSUSE
* lua51
* xdg-utils
* zenity
* curl
Gentoo
------
@@ -71,13 +71,3 @@ Gentoo
* dev-lang/lua-5.1.5
* x11-misc/xdg-utils
* gnome-extra/zenity
* net-misc/curl
OSX
=====
Use `make dependencies` to map the native libraries to your system.
To compile OpenRA, run `make` from the command line.
Run with `mono --debug OpenRA.Game.exe`.

186
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/MaxMind.Db.dll thirdparty/download/Eluant.dll thirdparty/download/SmarIrc4net.dll
NUNIT_LIBS_PATH :=
NUNIT_LIBS := $(NUNIT_LIBS_PATH)nunit.framework.dll
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
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`)
@@ -108,7 +100,7 @@ endif
game_SRCS := $(shell find OpenRA.Game/ -iname '*.cs')
game_TARGET = OpenRA.Game.exe
game_KIND = winexe
game_LIBS = $(COMMON_LIBS) $(game_DEPS) thirdparty/download/SharpFont.dll thirdparty/download/Open.Nat.dll
game_LIBS = $(COMMON_LIBS) $(game_DEPS) thirdparty/download/SharpFont.dll
game_FLAGS = -win32icon:OpenRA.Game/OpenRA.ico
PROGRAMS += game
game: $(game_TARGET)
@@ -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,24 +129,23 @@ 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)
STD_MOD_DEPS = $(STD_MOD_LIBS)
# Red Alert
mod_ra_SRCS := $(shell find OpenRA.Mods.RA/ -iname '*.cs')
mod_ra_TARGET = mods/ra/OpenRA.Mods.RA.dll
mod_ra_KIND = library
mod_ra_DEPS = $(STD_MOD_DEPS) $(mod_common_TARGET)
mod_ra_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_common_TARGET)
PROGRAMS += mod_ra
mod_ra: $(mod_ra_TARGET)
# Command and Conquer
mod_cnc_SRCS := $(shell find OpenRA.Mods.Cnc/ -iname '*.cs')
mod_cnc_TARGET = mods/common/OpenRA.Mods.Cnc.dll
mod_cnc_TARGET = mods/cnc/OpenRA.Mods.Cnc.dll
mod_cnc_KIND = library
mod_cnc_DEPS = $(STD_MOD_DEPS) $(mod_common_TARGET)
mod_cnc_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_common_TARGET)
@@ -164,6 +161,15 @@ mod_d2k_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_common_TARGET)
PROGRAMS += mod_d2k
mod_d2k: $(mod_d2k_TARGET)
# Tiberian Sun
mod_ts_SRCS := $(shell find OpenRA.Mods.TS/ -iname '*.cs')
mod_ts_TARGET = mods/ts/OpenRA.Mods.TS.dll
mod_ts_KIND = library
mod_ts_DEPS = $(STD_MOD_DEPS) $(mod_common_TARGET)
mod_ts_LIBS = $(COMMON_LIBS) $(STD_MOD_LIBS) $(mod_common_TARGET)
PROGRAMS += mod_ts
mod_ts: $(mod_ts_TARGET)
check-scripts:
@echo
@echo "Checking for Lua syntax errors..."
@@ -171,9 +177,6 @@ check-scripts:
@luac -p $(shell find lua/* -iname '*.lua')
check: utility mods
@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.Game..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Game
@@ -181,45 +184,32 @@ 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
@echo "Checking for code style violations in OpenRA.Mods.Common..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Mods.Common
@echo
@echo "Checking for code style violations in OpenRA.Mods.RA..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Mods.RA
@echo
@echo "Checking for code style violations in OpenRA.Mods.Cnc..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Mods.Cnc
@echo
@echo "Checking for code style violations in OpenRA.Mods.D2k..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Mods.D2k
@echo
@echo "Checking for code style violations in OpenRA.Mods.TS..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Mods.TS
@echo
@echo "Checking for code style violations in OpenRA.Utility..."
@mono --debug OpenRA.Utility.exe ra --check-code-style OpenRA.Utility
@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 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
@@ -256,18 +246,8 @@ 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
@command -v $(CSC) >/dev/null || (echo "Mono is not installed. Please install Mono from http://www.mono-project.com/download/ before building OpenRA."; exit 1)
@echo CSC fixheader.exe
@$(CSC) packaging/fixheader.cs $(CSFLAGS) -out:fixheader.exe -t:exe $(COMMON_LIBS:%=-r:%)
@@ -295,13 +275,13 @@ $(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog))))
#
default: core
core: dependencies game platforms mods utility server
core: game platforms mods utility
tools: gamemonitor
package: all-dependencies core tools docs version
mods: mod_common mod_cnc mod_d2k
mods: mod_common mod_ra mod_cnc mod_d2k mod_ts
all: dependencies core tools
@@ -316,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
@@ -339,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
@@ -364,9 +343,10 @@ install-core: default
@$(INSTALL_DIR) "$(DATA_INSTALL_DIR)/mods"
@$(CP_R) mods/common "$(DATA_INSTALL_DIR)/mods/"
@$(INSTALL_PROGRAM) $(mod_common_TARGET) "$(DATA_INSTALL_DIR)/mods/common"
@$(INSTALL_PROGRAM) $(mod_cnc_TARGET) "$(DATA_INSTALL_DIR)/mods/common"
@$(CP_R) mods/cnc "$(DATA_INSTALL_DIR)/mods/"
@$(INSTALL_PROGRAM) $(mod_cnc_TARGET) "$(DATA_INSTALL_DIR)/mods/cnc"
@$(CP_R) mods/ra "$(DATA_INSTALL_DIR)/mods/"
@$(INSTALL_PROGRAM) $(mod_ra_TARGET) "$(DATA_INSTALL_DIR)/mods/ra"
@$(CP_R) mods/d2k "$(DATA_INSTALL_DIR)/mods/"
@$(INSTALL_PROGRAM) $(mod_d2k_TARGET) "$(DATA_INSTALL_DIR)/mods/d2k"
@$(CP_R) mods/modchooser "$(DATA_INSTALL_DIR)/mods/"
@@ -379,14 +359,16 @@ 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)"
@$(INSTALL_PROGRAM) SharpFont.dll "$(DATA_INSTALL_DIR)"
@$(CP) SharpFont.dll.config "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) Open.Nat.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) Mono.Nat.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) MaxMind.Db.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) MaxMind.GeoIP2.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) Newtonsoft.Json.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) RestSharp.dll "$(DATA_INSTALL_DIR)"
@$(INSTALL_PROGRAM) SmarIrc4net.dll "$(DATA_INSTALL_DIR)"
ifneq ($(UNAME_S),Darwin)
@@ -413,7 +395,6 @@ install-linux-mime:
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/applications"
@$(INSTALL_DATA) packaging/linux/openra-join-servers.desktop "$(DESTDIR)$(datadir)/applications"
@$(INSTALL_DATA) packaging/linux/openra-replays.desktop "$(DESTDIR)$(datadir)/applications"
@$(INSTALL_DATA) packaging/linux/openra-launch-mod.desktop "$(DESTDIR)$(datadir)/applications"
install-linux-appdata:
@$(INSTALL_DIR) "$(DESTDIR)$(datadir)/appdata/"
@@ -426,11 +407,10 @@ install-man-page: man-page
install-linux-scripts:
@echo "#!/bin/sh" > openra
@echo 'cd "$(gameinstalldir)"' >> openra
# Note: this relies on the non-standard -f flag implemented by gnu readlink
ifeq ($(DEBUG), $(filter $(DEBUG),false no n off 0))
@echo 'mono OpenRA.Game.exe Engine.LaunchPath="$$(readlink -f $$0)" "$$@"' >> openra
@echo 'mono OpenRA.Game.exe "$$@"' >> openra
else
@echo 'mono --debug OpenRA.Game.exe Engine.LaunchPath="$$(readlink -f $$0)" "$$@"' >> openra
@echo 'mono --debug OpenRA.Game.exe "$$@"' >> openra
endif
@echo 'if [ $$? != 0 -a $$? != 1 ]' >> openra
@echo 'then' >> openra
@@ -443,26 +423,10 @@ 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)/applications/openra-join-servers.desktop"
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra-launch-mod.desktop"
@-$(RM_F) "$(DESTDIR)$(datadir)/applications/openra-join-servers.desktop"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/16x16/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/32x32/apps/openra.png"
@-$(RM_F) "$(DESTDIR)$(datadir)/icons/hicolor/48x48/apps/openra.png"
@@ -473,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
@@ -515,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,262 +1,38 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.Traits;
namespace OpenRA.Activities
{
public enum ActivityState { Queued, Active, Done, Canceled }
/*
* Activities are actions carried out by actors during each tick.
*
* Activities exist in a graph data structure built up amongst themselves. Each activity has a parent activity,
* optionally child activities, and usually a next activity. An actor's CurrentActivity is a pointer into that graph
* and moves through it as activities run.
*
* There are two kinds of activities, the base activity and composite activities. They differ in the way their children
* are run: while a base activity is responsible for running its children itself, a composite activity relies on the actor's
* activity-running code. Therefore, the actor's CurrentActivity stays on the base activity while it runs its children. With
* composite activities however, the CurrentActivity moves through the list of children as they run.
*
*
* Things to be aware of when writing activities:
*
* - Use "return NextActivity" at least once somewhere in the tick method.
* - Do not use "return new SomeActivity()" as that will break the graph. Queue the new activity and use "return NextActivity" instead.
* - Do not "reuse" (with "SequenceActivities", for example) activity objects that have already finished running.
* Queue a new instance instead.
* - Avoid calling actor.CancelActivity(). It is almost always a bug. Call activity.Cancel() instead.
* - A composite activity will run at least twice. The first time when it returns its children,
* the second time when its last child returns its Parent.
* - Do not return the Parent explicitly unless you have an extremly good reason. "return NextActivity"
* will do the right thing in all circumstances.
* - You do not need to care about the ChildActivity pointer advancing through the list of children,
* the activity code already takes care of that.
* - If you want to check whether there are any follow-up activities queued, check against "NextInQueue"
* in favour of "NextActivity" to avoid checking against the Parent activity.
*
*
* Guide when to use which kind of activity:
*
* - The activity does not have any children -> base activity
* - The activity needs to run preparatory steps during each tick before its children can be run -> base activity
* - The activity or the actor is left in a bogus state when one of the child activities is canceled -> base activity
* - The activity's children are self-contained and can run independently of the parent -> composite activity
* - The activity does not have any or little logic of its own, but is just composed of sub-steps -> composite activity
*/
public abstract class Activity
{
public ActivityState State { get; private set; }
/// <summary>
/// Returns the top-most activity *from the point of view of the calling activity*. Note that the root activity
/// can and likely will have next activities of its own, which would in turn be the root for their children.
/// </summary>
public Activity RootActivity
{
get
{
var p = this;
while (p.ParentActivity != null)
p = p.ParentActivity;
return p;
}
}
Activity parentActivity;
public Activity ParentActivity
{
get
{
return parentActivity;
}
protected set
{
parentActivity = value;
var next = NextInQueue;
if (next != null)
next.ParentActivity = parentActivity;
}
}
Activity childActivity;
protected Activity ChildActivity
{
get
{
return childActivity != null && childActivity.State < ActivityState.Done ? childActivity : null;
}
set
{
if (value == this || value == ParentActivity || value == NextInQueue)
childActivity = null;
else
{
childActivity = value;
if (childActivity != null)
childActivity.ParentActivity = this;
}
}
}
Activity nextActivity;
/// <summary>
/// The getter will return either the next activity or, if there is none, the parent one.
/// </summary>
public virtual Activity NextActivity
{
get
{
return nextActivity != null ? nextActivity : ParentActivity;
}
set
{
if (value == this || value == ParentActivity || (value != null && value.ParentActivity == this))
nextActivity = null;
else
{
nextActivity = value;
if (nextActivity != null)
nextActivity.ParentActivity = ParentActivity;
}
}
}
/// <summary>
/// The getter will return the next activity on the same level _only_, in contrast to NextActivity.
/// Use this to check whether there are any follow-up activities queued.
/// </summary>
public Activity NextInQueue
{
get { return nextActivity; }
set { NextActivity = value; }
}
public bool IsInterruptible { get; protected set; }
public bool IsCanceled { get { return State == ActivityState.Canceled; } }
public Activity()
{
IsInterruptible = true;
}
public Activity TickOuter(Actor self)
{
if (State == ActivityState.Done && Game.Settings.Debug.StrictActivityChecking)
throw new InvalidOperationException("Actor {0} attempted to tick activity {1} after it had already completed.".F(self, this.GetType()));
if (State == ActivityState.Queued)
{
OnFirstRun(self);
State = ActivityState.Active;
}
var ret = Tick(self);
if (ret == null || (ret != this && ret.ParentActivity != this))
{
// Make sure that the Parent's ChildActivity pointer is moved forwards as the child queue advances.
// The Child's ParentActivity will be set automatically during assignment.
if (ParentActivity != null && ParentActivity != ret)
ParentActivity.ChildActivity = ret;
if (State != ActivityState.Canceled)
State = ActivityState.Done;
OnLastRun(self);
}
return ret;
}
public Activity NextActivity { get; set; }
protected bool IsCanceled { get; private set; }
public abstract Activity Tick(Actor self);
/// <summary>
/// Runs once immediately before the first Tick() execution.
/// </summary>
protected virtual void OnFirstRun(Actor self) { }
/// <summary>
/// Runs once immediately after the last Tick() execution.
/// </summary>
protected virtual void OnLastRun(Actor self) { }
public virtual bool Cancel(Actor self)
public virtual void Cancel(Actor self)
{
if (!IsInterruptible)
return false;
if (ChildActivity != null && !ChildActivity.Cancel(self))
return false;
State = ActivityState.Canceled;
IsCanceled = true;
NextActivity = null;
ChildActivity = null;
return true;
}
public virtual void Queue(Activity activity)
{
if (NextInQueue != null)
NextInQueue.Queue(activity);
if (NextActivity != null)
NextActivity.Queue(activity);
else
NextInQueue = activity;
}
public virtual void QueueChild(Activity activity)
{
if (ChildActivity != null)
ChildActivity.Queue(activity);
else
ChildActivity = activity;
}
/// <summary>
/// Prints the activity tree, starting from the root or optionally from a given origin.
///
/// Call this method from any place that's called during a tick, such as the Tick() method itself or
/// the Before(First|Last)Run() methods. The origin activity will be marked in the output.
/// </summary>
/// <param name="origin">Activity from which to start traversing, and which to mark. If null, mark the calling activity, and start traversal from the root.</param>
/// <param name="level">Initial level of indentation.</param>
protected void PrintActivityTree(Activity origin = null, int level = 0)
{
if (origin == null)
RootActivity.PrintActivityTree(this);
else
{
Console.Write(new string(' ', level * 2));
if (origin == this)
Console.Write("*");
Console.WriteLine(this.GetType().ToString().Split('.').Last());
if (ChildActivity != null)
ChildActivity.PrintActivityTree(origin, level + 1);
if (NextInQueue != null)
NextInQueue.PrintActivityTree(origin, level);
}
NextActivity = activity;
}
public virtual IEnumerable<Target> GetTargets(Actor self)
@@ -265,34 +41,11 @@ namespace OpenRA.Activities
}
}
/// <summary>
/// In contrast to the base activity class, which is responsible for running its children itself,
/// composite activities rely on the actor's activity-running logic for their children.
/// </summary>
public abstract class CompositeActivity : Activity
{
/// <summary>
/// The getter will return the first non-null value of either child, next or parent activity, in that order, or ultimately null.
/// </summary>
public override Activity NextActivity
{
get
{
if (ChildActivity != null)
return ChildActivity;
else if (NextInQueue != null)
return NextInQueue;
else
return ParentActivity;
}
}
}
public static class ActivityExts
{
public static IEnumerable<Target> GetTargetQueue(this Actor self)
{
return self.CurrentActivity
return self.GetCurrentActivity()
.Iterate(u => u.NextActivity)
.TakeWhile(u => u != null)
.SelectMany(u => u.GetTargets(self));

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,18 +15,25 @@ namespace OpenRA.Activities
public class CallFunc : Activity
{
public CallFunc(Action a) { this.a = a; }
public CallFunc(Action a, bool interruptible)
public CallFunc(Action a, bool interruptable)
{
this.a = a;
IsInterruptible = interruptible;
this.interruptable = interruptable;
}
Action a;
bool interruptable;
public override Activity Tick(Actor self)
{
if (a != null) a();
return NextActivity;
}
public override void Cancel(Actor self)
{
if (interruptable)
base.Cancel(self);
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,25 +24,18 @@ 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;
public readonly uint ActorID;
public Player Owner { get; internal set; }
public Player Owner { get; set; }
public bool IsInWorld { get; internal set; }
public bool Disposed { get; private set; }
public Activity CurrentActivity { get; private set; }
Activity currentActivity;
public Group Group;
public int Generation;
@@ -52,9 +44,8 @@ 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 IsIdle { get { return currentActivity == null; } }
public bool IsDead { get { return Disposed || (health != null && health.IsDead); } }
public CPos Location { get { return OccupiesSpace.TopLeft; } }
@@ -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;
@@ -200,18 +171,21 @@ namespace OpenRA
public void QueueActivity(Activity nextActivity)
{
if (CurrentActivity == null)
CurrentActivity = nextActivity;
if (currentActivity == null)
currentActivity = nextActivity;
else
CurrentActivity.RootActivity.Queue(nextActivity);
currentActivity.Queue(nextActivity);
}
public bool CancelActivity()
public void CancelActivity()
{
if (CurrentActivity != null)
return CurrentActivity.RootActivity.Cancel(this);
if (currentActivity != null)
currentActivity.Cancel(this);
}
return true;
public Activity GetCurrentActivity()
{
return currentActivity;
}
public override int GetHashCode()
@@ -232,7 +206,6 @@ namespace OpenRA
public override string ToString()
{
// PERF: Avoid format strings.
var name = Info.Name + " " + ActorID;
if (!IsInWorld)
name += " (not in world)";
@@ -298,11 +271,11 @@ namespace OpenRA
Owner = newOwner;
Generation++;
foreach (var t in TraitsImplementing<INotifyOwnerChanged>())
t.OnOwnerChanged(this, oldOwner, newOwner);
if (wasInWorld)
w.Add(this);
foreach (var t in this.TraitsImplementing<INotifyOwnerChanged>())
t.OnOwnerChanged(this, oldOwner, newOwner);
});
}
@@ -314,12 +287,12 @@ namespace OpenRA
return (health == null) ? DamageState.Undamaged : health.DamageState;
}
public void InflictDamage(Actor attacker, Damage damage)
public void InflictDamage(Actor attacker, int damage, IWarhead warhead)
{
if (Disposed || health == null)
return;
health.InflictDamage(this, attacker, damage, false);
health.InflictDamage(this, attacker, damage, warhead, false);
}
public void Kill(Actor attacker)
@@ -332,7 +305,6 @@ namespace OpenRA
public bool IsDisabled()
{
// PERF: Avoid LINQ.
foreach (var disable in disables)
if (disable.Disabled)
return true;
@@ -341,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;
@@ -349,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;
@@ -394,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-2017 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,25 +18,26 @@ namespace OpenRA
public struct CPos : IScriptBindable, ILuaAdditionBinding, ILuaSubtractionBinding, ILuaEqualityBinding, ILuaTableBinding, IEquatable<CPos>
{
public readonly int X, Y;
public readonly byte Layer;
public CPos(int x, int y) { X = x; Y = y; Layer = 0; }
public CPos(int x, int y, byte layer) { X = x; Y = y; Layer = layer; }
public static readonly CPos Zero = new CPos(0, 0, 0);
public CPos(int x, int y) { X = x; Y = y; }
public static readonly CPos Zero = new CPos(0, 0);
public static explicit operator CPos(int2 a) { return new CPos(a.X, a.Y); }
public static CPos operator +(CVec a, CPos b) { return new CPos(a.X + b.X, a.Y + b.Y, b.Layer); }
public static CPos operator +(CPos a, CVec b) { return new CPos(a.X + b.X, a.Y + b.Y, a.Layer); }
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y, a.Layer); }
public static CPos operator +(CVec a, CPos b) { return new CPos(a.X + b.X, a.Y + b.Y); }
public static CPos operator +(CPos a, CVec b) { return new CPos(a.X + b.X, a.Y + b.Y); }
public static CPos operator -(CPos a, CVec b) { return new CPos(a.X - b.X, a.Y - b.Y); }
public static CVec operator -(CPos a, CPos b) { return new CVec(a.X - b.X, a.Y - b.Y); }
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y && me.Layer == other.Layer; }
public static bool operator ==(CPos me, CPos other) { return me.X == other.X && me.Y == other.Y; }
public static bool operator !=(CPos me, CPos other) { return !(me == other); }
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode() ^ Layer.GetHashCode(); }
public static CPos Max(CPos a, CPos b) { return new CPos(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y)); }
public static CPos Min(CPos a, CPos b) { return new CPos(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y)); }
public bool Equals(CPos other) { return X == other.X && Y == other.Y && Layer == other.Layer; }
public override int GetHashCode() { return X.GetHashCode() ^ Y.GetHashCode(); }
public bool Equals(CPos other) { return X == other.X && Y == other.Y; }
public override bool Equals(object obj) { return obj is CPos && Equals((CPos)obj); }
public override string ToString() { return X + "," + Y; }
@@ -73,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);
@@ -82,30 +82,17 @@ namespace OpenRA
public LuaValue Subtract(LuaRuntime runtime, LuaValue left, LuaValue right)
{
CPos a;
var rightType = right.WrappedClrType();
if (!left.TryGetClrValue(out a))
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
CVec 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));
if (rightType == typeof(CPos))
{
CPos b;
right.TryGetClrValue(out b);
return new LuaCustomClrObject(a - b);
}
else if (rightType == typeof(CVec))
{
CVec b;
right.TryGetClrValue(out b);
return new LuaCustomClrObject(a - b);
}
throw new LuaException("Attempted to call CPos.Subtract(CPos, (CPos|CVec)) with invalid arguments ({0}, {1})".F(left.WrappedClrType().Name, rightType.Name));
return new LuaCustomClrObject(a - b);
}
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;
@@ -119,7 +106,6 @@ namespace OpenRA
{
case "X": return X;
case "Y": return Y;
case "Layer": return Layer;
default: throw new LuaException("CPos does not define a member '{0}'".F(key));
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
#region Copyright & License Information
/*
* Copyright 2007-2017 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

@@ -0,0 +1,37 @@
#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.Collections.Generic;
namespace OpenRA
{
// Referenced from ModMetadata, so needs to be in OpenRA.Game :(
public class ContentInstaller : IGlobalModData
{
public readonly string[] TestFiles = { };
public readonly string[] DiskTestFiles = { };
public readonly string PackageToExtractFromCD = null;
public readonly bool OverwriteFiles = true;
public readonly Dictionary<string, string[]> CopyFilesFromCD = new Dictionary<string, string[]>();
public readonly Dictionary<string, string[]> ExtractFilesFromCD = new Dictionary<string, string[]>();
public readonly string PackageMirrorList = null;
public readonly string MusicPackageMirrorList = null;
public readonly int ShippedSoundtracks = 0;
/// <summary> InstallShield .CAB file IDs, used to extract Mod-specific files. </summary>
public readonly HashSet<int> InstallShieldCABFileIds = new HashSet<int>();
/// <summary> InstallShield .CAB file IDs, used to extract Mod-specific archives and extract contents of ExtractFilesFromCD. </summary>
public readonly HashSet<string> InstallShieldCABFilePackageIds = new HashSet<string>();
}
}

View File

@@ -1,38 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
namespace OpenRA
{
public static class CryptoUtil
{
public static string SHA1Hash(Stream data)
{
using (var csp = SHA1.Create())
return new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray());
}
public static string SHA1Hash(byte[] data)
{
using (var csp = SHA1.Create())
return new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray());
}
public static string SHA1Hash(string data)
{
return SHA1Hash(Encoding.UTF8.GetBytes(data));
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,8 +16,8 @@ namespace OpenRA
{
public class Download
{
readonly object syncObject = new object();
WebClient wc;
bool cancelled;
public static string FormatErrorMessage(Exception e)
{
@@ -28,8 +27,6 @@ namespace OpenRA
switch (ex.Status)
{
case WebExceptionStatus.RequestCanceled:
return "Cancelled";
case WebExceptionStatus.NameResolutionFailure:
return "DNS lookup failed";
case WebExceptionStatus.Timeout:
@@ -43,42 +40,40 @@ namespace OpenRA
}
}
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs> onComplete)
public Download(string url, string path, Action<DownloadProgressChangedEventArgs> onProgress, Action<AsyncCompletedEventArgs, bool> onComplete)
{
lock (syncObject)
{
wc = new WebClient { Proxy = null };
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadFileCompleted += (_, a) => { DisposeWebClient(); onComplete(a); };
wc.DownloadFileAsync(new Uri(url), path);
}
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadFileCompleted += (_, a) => onComplete(a, cancelled);
Game.OnQuit += Cancel;
wc.DownloadFileCompleted += (_, a) => { Game.OnQuit -= Cancel; };
wc.DownloadFileAsync(new Uri(url), path);
}
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs> onComplete)
public Download(string url, Action<DownloadProgressChangedEventArgs> onProgress, Action<DownloadDataCompletedEventArgs, bool> onComplete)
{
lock (syncObject)
{
wc = new WebClient { Proxy = null };
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadDataCompleted += (_, a) => { DisposeWebClient(); onComplete(a); };
wc.DownloadDataAsync(new Uri(url));
}
wc = new WebClient();
wc.Proxy = null;
wc.DownloadProgressChanged += (_, a) => onProgress(a);
wc.DownloadDataCompleted += (_, a) => onComplete(a, cancelled);
Game.OnQuit += Cancel;
wc.DownloadDataCompleted += (_, a) => { Game.OnQuit -= Cancel; };
wc.DownloadDataAsync(new Uri(url));
}
void DisposeWebClient()
public void Cancel()
{
lock (syncObject)
{
wc.Dispose();
wc = null;
}
}
public void CancelAsync()
{
lock (syncObject)
if (wc != null)
wc.CancelAsync();
Game.OnQuit -= Cancel;
wc.CancelAsync();
wc.Dispose();
cancelled = true;
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
#region Copyright & License Information
/*
* Copyright 2007-2017 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-2017 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,46 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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 OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Effects
{
public class DelayedImpact : IEffect
{
readonly Target target;
readonly Actor firedBy;
readonly IEnumerable<int> damageModifiers;
readonly IWarhead wh;
int delay;
public DelayedImpact(int delay, IWarhead wh, Target target, Actor firedBy, IEnumerable<int> damageModifiers)
{
this.wh = wh;
this.delay = delay;
this.target = target;
this.firedBy = firedBy;
this.damageModifiers = damageModifiers;
}
public void Tick(World world)
{
if (--delay <= 0)
world.AddFrameEndTask(w => { w.Remove(this); wh.DoImpact(target, firedBy, damageModifiers); });
}
public IEnumerable<IRenderable> Render(WorldRenderer wr) { yield break; }
}
}

View File

@@ -1,20 +1,18 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.Effects;
using OpenRA.Graphics;
namespace OpenRA.Mods.Common.Effects
namespace OpenRA.Effects
{
public class FlashTarget : IEffect
{

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,6 +18,4 @@ namespace OpenRA.Effects
void Tick(World world);
IEnumerable<IRenderable> Render(WorldRenderer r);
}
public interface IEffectAboveShroud { IEnumerable<IRenderable> RenderAboveShroud(WorldRenderer wr); }
}

View File

@@ -0,0 +1,40 @@
#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.Collections.Generic;
using OpenRA.Graphics;
namespace OpenRA.Effects
{
public class SpriteEffect : IEffect
{
readonly string palette;
readonly Animation anim;
readonly WPos pos;
public SpriteEffect(WPos pos, World world, string image, string palette)
{
this.pos = pos;
this.palette = palette;
anim = new Animation(world, image);
anim.PlayThen("idle", () => world.AddFrameEndTask(w => w.Remove(this)));
}
public void Tick(World world)
{
anim.Tick();
}
public IEnumerable<IRenderable> Render(WorldRenderer wr)
{
return anim.Render(pos, WVec.Zero, 0, wr.Palette(palette), 1f / wr.Viewport.Zoom);
}
}
}

View File

@@ -1,157 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using OpenRA.Graphics;
namespace OpenRA
{
public class ExternalMod
{
public readonly string Id;
public readonly string Version;
public readonly string Title;
public readonly string LaunchPath;
public readonly string[] LaunchArgs;
public Sprite Icon { get; internal set; }
public static string MakeKey(Manifest mod) { return MakeKey(mod.Id, mod.Metadata.Version); }
public static string MakeKey(ExternalMod mod) { return MakeKey(mod.Id, mod.Version); }
public static string MakeKey(string modId, string modVersion) { return modId + "-" + modVersion; }
}
public class ExternalMods : IReadOnlyDictionary<string, ExternalMod>
{
readonly Dictionary<string, ExternalMod> mods;
readonly SheetBuilder sheetBuilder;
readonly string launchPath;
public ExternalMods(string launchPath)
{
// Process.Start requires paths to not be quoted, even if they contain spaces
if (launchPath.First() == '"' && launchPath.Last() == '"')
launchPath = launchPath.Substring(1, launchPath.Length - 2);
this.launchPath = launchPath;
sheetBuilder = new SheetBuilder(SheetType.BGRA, 256);
mods = LoadMods();
}
Dictionary<string, ExternalMod> LoadMods()
{
var ret = new Dictionary<string, ExternalMod>();
var supportPath = Platform.ResolvePath(Path.Combine("^", "ModMetadata"));
if (!Directory.Exists(supportPath))
return ret;
foreach (var path in Directory.GetFiles(supportPath, "*.yaml"))
{
try
{
var yaml = MiniYaml.FromStream(File.OpenRead(path), path).First().Value;
var mod = FieldLoader.Load<ExternalMod>(yaml);
var iconNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Icon");
if (iconNode != null && !string.IsNullOrEmpty(iconNode.Value.Value))
{
using (var stream = new MemoryStream(Convert.FromBase64String(iconNode.Value.Value)))
using (var bitmap = new Bitmap(stream))
mod.Icon = sheetBuilder.Add(bitmap);
}
ret.Add(ExternalMod.MakeKey(mod), mod);
}
catch (Exception e)
{
Log.Write("debug", "Failed to parse mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
}
}
return ret;
}
internal void Register(Manifest mod)
{
if (mod.Metadata.Hidden)
return;
var iconData = "";
using (var stream = mod.Package.GetStream("icon.png"))
if (stream != null)
iconData = Convert.ToBase64String(stream.ReadAllBytes());
var key = ExternalMod.MakeKey(mod);
var yaml = new List<MiniYamlNode>()
{
new MiniYamlNode("Registration", new MiniYaml("", new List<MiniYamlNode>()
{
new MiniYamlNode("Id", mod.Id),
new MiniYamlNode("Version", mod.Metadata.Version),
new MiniYamlNode("Title", mod.Metadata.Title),
new MiniYamlNode("Icon", iconData),
new MiniYamlNode("LaunchPath", launchPath),
new MiniYamlNode("LaunchArgs", "Game.Mod=" + mod.Id)
}))
};
var supportPath = Platform.ResolvePath(Path.Combine("^", "ModMetadata"));
Directory.CreateDirectory(supportPath);
File.WriteAllLines(Path.Combine(supportPath, key + ".yaml"), yaml.ToLines(false).ToArray());
// Clean up stale mod registrations:
// - LaunchPath no longer exists (uninstalled)
// - LaunchPath and mod match the current mod, but version differs (newer version installed on top)
List<string> toRemove = null;
foreach (var kv in mods)
{
var k = kv.Key;
var m = kv.Value;
if (!File.Exists(m.LaunchPath) || (m.LaunchPath == launchPath && m.Id == mod.Id && k != key))
{
Log.Write("debug", "Removing stale mod metadata entry '{0}'", k);
if (toRemove == null)
toRemove = new List<string>();
toRemove.Add(k);
var path = Path.Combine(supportPath, k + ".yaml");
try
{
File.Delete(path);
}
catch (Exception e)
{
Log.Write("debug", "Failed to remove mod metadata file '{0}'", path);
Log.Write("debug", e.ToString());
}
}
}
if (toRemove != null)
foreach (var r in toRemove)
mods.Remove(r);
}
public ExternalMod this[string key] { get { return mods[key]; } }
public int Count { get { return mods.Count; } }
public ICollection<string> Keys { get { return mods.Keys; } }
public ICollection<ExternalMod> Values { get { return mods.Values; } }
public bool ContainsKey(string key) { return mods.ContainsKey(key); }
public IEnumerator<KeyValuePair<string, ExternalMod>> GetEnumerator() { return mods.GetEnumerator(); }
public bool TryGetValue(string key, out ExternalMod value) { return mods.TryGetValue(key, out value); }
IEnumerator IEnumerable.GetEnumerator() { return mods.GetEnumerator(); }
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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());
@@ -489,11 +478,6 @@ namespace OpenRA
return int.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
}
public static bool TryParseInt64Invariant(string s, out long i)
{
return long.TryParse(s, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out i);
}
public static bool IsTraitEnabled(this object trait)
{
return trait as IDisabledTrait == null || !(trait as IDisabledTrait).IsTraitDisabled;

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,20 +14,16 @@ using System.ComponentModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
public static class FieldLoader
{
[Serializable]
public class MissingFieldsException : YamlException
{
public readonly string[] Missing;
@@ -47,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) =>
@@ -224,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);
}
@@ -235,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;
}
@@ -250,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),
@@ -376,44 +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 == typeof(ConditionExpression))
{
if (value != null)
{
try
{
return new ConditionExpression(value);
}
catch (InvalidDataException e)
{
throw new YamlException(e.Message);
}
}
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType.IsEnum)
{
try
@@ -455,9 +423,7 @@ namespace OpenRA
if (value == null)
return Array.CreateInstance(fieldType.GetElementType(), 0);
var options = field != null && field.HasAttribute<AllowEmptyEntriesAttribute>() ?
StringSplitOptions.None : StringSplitOptions.RemoveEmptyEntries;
var parts = value.Split(new char[] { ',' }, options);
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var ret = Array.CreateInstance(fieldType.GetElementType(), parts.Length);
for (var i = 0; i < parts.Length; i++)
@@ -528,26 +494,6 @@ namespace OpenRA
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(float3))
{
if (value != null)
{
var parts = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
float x = 0;
float y = 0;
float z = 0;
float.TryParse(parts[0], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out x);
float.TryParse(parts[1], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out y);
// z component is optional for compatibility with older float2 definitions
if (parts.Length > 2)
float.TryParse(parts[2], NumberStyles.Float, NumberFormatInfo.InvariantInfo, out z);
return new float3(x, y, z);
}
return InvalidValueAction(value, fieldType, fieldName);
}
else if (fieldType == typeof(Rectangle))
{
if (value != null)
@@ -681,13 +627,6 @@ namespace OpenRA
: base(true, true) { }
}
[AttributeUsage(AttributeTargets.Field)]
public sealed class AllowEmptyEntriesAttribute : SerializeAttribute
{
public AllowEmptyEntriesAttribute()
: base(allowEmptyEntries: true) { }
}
[AttributeUsage(AttributeTargets.Field)]
public sealed class LoadUsingAttribute : SerializeAttribute
{
@@ -711,13 +650,11 @@ namespace OpenRA
public bool FromYamlKey;
public bool DictionaryFromYamlKey;
public bool Required;
public bool AllowEmptyEntries;
public SerializeAttribute(bool serialize = true, bool required = false, bool allowEmptyEntries = false)
public SerializeAttribute(bool serialize = true, bool required = false)
{
Serialize = serialize;
Required = required;
AllowEmptyEntries = allowEmptyEntries;
}
internal Func<MiniYaml, object> GetLoader(Type type)
@@ -785,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-2017 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,18 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
{
[Flags]
enum SoundFlags
@@ -37,17 +36,16 @@ namespace OpenRA.Mods.Common.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 AudReader
public static class AudLoader
{
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,
@@ -66,14 +64,14 @@ namespace OpenRA.Mods.Common.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;
@@ -119,21 +117,13 @@ namespace OpenRA.Mods.Common.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;
@@ -163,8 +153,7 @@ namespace OpenRA.Mods.Common.FileFormats
dataSize -= 8 + chunk.CompressedSize;
}
rawData = output;
return true;
return output;
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
@@ -57,19 +56,16 @@ namespace OpenRA.FileFormats
static Huffman lencode = new Huffman(lenlen, lenlen.Length, 16);
static Huffman distcode = new Huffman(distlen, distlen.Length, 64);
/// <summary>PKWare Compression Library stream.</summary>
/// <param name="input">Compressed input stream.</param>
/// <param name="output">Stream to write the decompressed output.</param>
/// <param name="onProgress">Progress callback, invoked with (read bytes, written bytes).</param>
public static void Decompress(Stream input, Stream output, Action<long, long> onProgress = null)
// Decode PKWare Compression Library stream.
public static byte[] Decompress(byte[] src)
{
var br = new BitReader(input);
var br = new BitReader(src);
// Are literals coded?
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
@@ -81,9 +77,7 @@ namespace OpenRA.FileFormats
ushort next = 0; // index of next write location in out[]
var first = true; // true to check distances (for first 4K)
var outBuffer = new byte[MAXWIN]; // output buffer and sliding window
var inputStart = input.Position;
var outputStart = output.Position;
var ms = new MemoryStream();
// decode literals and length/distance pairs
do
@@ -99,10 +93,7 @@ namespace OpenRA.FileFormats
if (len == 519)
{
for (var i = 0; i < next; i++)
output.WriteByte(outBuffer[i]);
if (onProgress != null)
onProgress(input.Position - inputStart, output.Position - outputStart);
ms.WriteByte(outBuffer[i]);
break;
}
@@ -145,12 +136,9 @@ namespace OpenRA.FileFormats
if (next == MAXWIN)
{
for (var i = 0; i < next; i++)
output.WriteByte(outBuffer[i]);
ms.WriteByte(outBuffer[i]);
next = 0;
first = false;
if (onProgress != null)
onProgress(input.Position - inputStart, output.Position - outputStart);
}
} while (len != 0);
}
@@ -162,18 +150,17 @@ namespace OpenRA.FileFormats
if (next == MAXWIN)
{
for (var i = 0; i < next; i++)
output.WriteByte(outBuffer[i]);
ms.WriteByte(outBuffer[i]);
next = 0;
first = false;
if (onProgress != null)
onProgress(input.Position - inputStart, output.Position - outputStart);
}
}
} while (true);
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
@@ -197,13 +184,14 @@ namespace OpenRA.FileFormats
class BitReader
{
readonly Stream stream;
byte bitBuffer = 0;
readonly byte[] src;
int offset = 0;
int bitBuffer = 0;
int bitCount = 0;
public BitReader(Stream stream)
public BitReader(byte[] src)
{
this.stream = stream;
this.src = src;
}
public int ReadBits(int count)
@@ -214,7 +202,7 @@ namespace OpenRA.FileFormats
{
if (bitCount == 0)
{
bitBuffer = stream.ReadUInt8();
bitBuffer = src[offset++];
bitCount = 8;
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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-2017 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-2017 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-2017 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-2017 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-2017 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-2017 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
#region Copyright & License Information
/*
* Copyright 2007-2017 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,17 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
{
struct ImaAdpcmChunk
{
@@ -29,9 +29,7 @@ namespace OpenRA.Mods.Common.FileFormats
}
}
// Mostly a duplicate of AudReader, with some difference when loading
// TODO: Investigate whether they can be fused to get rid of some duplication
public class ImaAdpcmReader
public static class ImaAdpcmLoader
{
static readonly int[] IndexAdjust = { -1, -1, -1, -1, 2, 4, 6, 8 };
static readonly int[] StepTable =

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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-2017 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-2017 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
@@ -79,27 +78,45 @@ namespace OpenRA.FileFormats
public static ReplayMetadata Read(string path)
{
using (var fs = new FileStream(path, FileMode.Open))
return Read(fs, path);
}
static ReplayMetadata Read(FileStream fs, string path)
{
if (!fs.CanSeek)
return null;
if (fs.Length < 20)
return null;
try
{
using (var fs = new FileStream(path, FileMode.Open))
fs.Seek(-(4 + 4), SeekOrigin.End);
var dataLength = fs.ReadInt32();
if (fs.ReadInt32() == MetaEndMarker)
{
if (!fs.CanSeek)
return null;
if (fs.Length < 20)
return null;
fs.Seek(-(4 + 4), SeekOrigin.End);
var dataLength = fs.ReadInt32();
if (fs.ReadInt32() == MetaEndMarker)
// go back by (end marker + length storage + data + version + start marker) bytes
fs.Seek(-(4 + 4 + dataLength + 4 + 4), SeekOrigin.Current);
try
{
// Go back by (end marker + length storage + data + version + start marker) bytes
fs.Seek(-(4 + 4 + dataLength + 4 + 4), SeekOrigin.Current);
return new ReplayMetadata(fs, path);
}
catch (YamlException ex)
{
Log.Write("debug", ex.ToString());
}
catch (InvalidOperationException ex)
{
Log.Write("debug", ex.ToString());
}
catch (NotSupportedException ex)
{
Log.Write("debug", ex.ToString());
}
}
}
catch (Exception ex)
catch (IOException ex)
{
Log.Write("debug", ex.ToString());
}

View File

@@ -1,19 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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-2017 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,65 +1,60 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
{
public class WavReader
public class WavLoader
{
public int FileSize;
public string Format;
public readonly int FileSize;
public readonly string Format;
public int FmtChunkSize;
public int AudioFormat;
public int Channels;
public int SampleRate;
public int ByteRate;
public int BlockAlign;
public int BitsPerSample;
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;
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();
@@ -69,17 +64,24 @@ namespace OpenRA.Mods.Common.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;
}
}
@@ -89,8 +91,6 @@ namespace OpenRA.Mods.Common.FileFormats
RawOutput = DecodeImaAdpcmData();
BitsPerSample = 16;
}
return true;
}
public static float WaveLength(Stream s)
@@ -152,7 +152,7 @@ namespace OpenRA.Mods.Common.FileFormats
{
// Decode 4 bytes (to 16 bytes of output) per channel
var chunk = s.ReadBytes(4);
var decoded = ImaAdpcmReader.LoadImaAdpcmSound(chunk, ref index[c], ref predictor[c]);
var decoded = ImaAdpcmLoader.LoadImaAdpcmSound(chunk, ref index[c], ref predictor[c]);
// Interleave output, one sample per channel
var outOffsetChannel = outOffset + (2 * c);
@@ -177,4 +177,4 @@ namespace OpenRA.Mods.Common.FileFormats
return output;
}
}
}
}

View File

@@ -1,54 +1,44 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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;
namespace OpenRA.FileFormats
{
public class XccGlobalDatabase : IDisposable
public class XccGlobalDatabase
{
public readonly string[] Entries;
readonly Stream s;
public XccGlobalDatabase(Stream stream)
public XccGlobalDatabase(Stream s)
{
s = stream;
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) { }
}
}
Entries = entries.ToArray();
}
public void Dispose()
{
s.Dispose();
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
#region Copyright & License Information
/*
* Copyright 2007-2017 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)1));
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)17));
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-2017 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-2017 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,303 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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 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>();
readonly IReadOnlyDictionary<string, Manifest> installedMods;
Cache<string, List<IReadOnlyPackage>> fileIndex = new Cache<string, List<IReadOnlyPackage>>(_ => new List<IReadOnlyPackage>());
public FileSystem(IReadOnlyDictionary<string, Manifest> installedMods)
{
this.installedMods = installedMods;
}
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);
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 (filename.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase) ||
filename.EndsWith(".oramap", StringComparison.InvariantCultureIgnoreCase))
{
using (var s = parent.GetStream(filename))
return new ZipFile(s, filename, parent);
}
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);
}
if (parent is Folder)
{
var subFolder = Platform.ResolvePath(Path.Combine(parent.Name, filename));
if (Directory.Exists(subFolder))
return new Folder(subFolder);
}
return null;
}
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);
Manifest mod;
if (!installedMods.TryGetValue(name, out mod))
throw new InvalidOperationException("Could not load mod '{0}'. Available mods: {1}".F(name, installedMods.Keys.JoinWith(", ")));
package = mod.Package;
modPackages.Add(package);
}
else
{
package = OpenPackage(name);
if (package == null)
throw new InvalidOperationException("Could not open package '{0}', file not found or its format is not supported.".F(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;
// The file should be in an explicit package (but we couldn't find it)
// Thus don't try to find it using the filename (which contains the invalid '|' char)
// This can be removed once the TODO below is resolved
if (explicitSplit > 0)
return false;
// 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,82 +1,82 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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;
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, StringComparison.Ordinal) && 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()
{
// 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);
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
using (var s = File.Create(filePath))
s.Write(contents, 0, contents.Length);
yield break;
}
public void Delete(string filename)
public IEnumerable<string> AllFileNames()
{
// 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 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);
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-2017 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-2017 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

@@ -0,0 +1,527 @@
#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.RegularExpressions;
using ICSharpCode.SharpZipLib.Zip.Compression;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
namespace OpenRA.FileSystem
{
public class InstallShieldCABExtractor : IDisposable, IFolder
{
const uint FileSplit = 0x1;
const uint FileObfuscated = 0x2;
const uint FileCompressed = 0x4;
const uint FileInvalid = 0x8;
const uint LinkPrev = 0x1;
const uint LinkNext = 0x2;
const uint MaxFileGroupCount = 71;
#region Nested Structs
struct FileGroup
{
public readonly string Name;
public readonly uint FirstFile;
public readonly uint LastFile;
public FileGroup(Stream reader, long offset)
{
var nameOffset = reader.ReadUInt32();
/* unknown */ reader.ReadBytes(18);
FirstFile = reader.ReadUInt32();
LastFile = reader.ReadUInt32();
reader.Seek(offset + (long)nameOffset, SeekOrigin.Begin);
Name = reader.ReadASCIIZ();
}
}
struct VolumeHeader
{
public readonly uint DataOffset;
public readonly uint DataOffsetHigh;
public readonly uint FirstFileIndex;
public readonly uint LastFileIndex;
public readonly uint FirstFileOffset;
public readonly uint FirstFileOffsetHigh;
public readonly uint FirstFileSizeExpanded;
public readonly uint FirstFileSizeExpandedHigh;
public readonly uint FirstFileSizeCompressed;
public readonly uint FirstFileSizeCompressedHigh;
public readonly uint LastFileOffset;
public readonly uint LastFileOffsetHigh;
public readonly uint LastFileSizeExpanded;
public readonly uint LastFileSizeExpandedHigh;
public readonly uint LastFileSizeCompressed;
public readonly uint LastFileSizeCompressedHigh;
public VolumeHeader(Stream reader)
{
DataOffset = reader.ReadUInt32();
DataOffsetHigh = reader.ReadUInt32();
FirstFileIndex = reader.ReadUInt32();
LastFileIndex = reader.ReadUInt32();
FirstFileOffset = reader.ReadUInt32();
FirstFileOffsetHigh = reader.ReadUInt32();
FirstFileSizeExpanded = reader.ReadUInt32();
FirstFileSizeExpandedHigh = reader.ReadUInt32();
FirstFileSizeCompressed = reader.ReadUInt32();
FirstFileSizeCompressedHigh = reader.ReadUInt32();
LastFileOffset = reader.ReadUInt32();
LastFileOffsetHigh = reader.ReadUInt32();
LastFileSizeExpanded = reader.ReadUInt32();
LastFileSizeExpandedHigh = reader.ReadUInt32();
LastFileSizeCompressed = reader.ReadUInt32();
LastFileSizeCompressedHigh = reader.ReadUInt32();
}
}
struct CommonHeader
{
public const long Size = 16;
public readonly uint Version;
public readonly uint VolumeInfo;
public readonly long CabDescriptorOffset;
public readonly uint CabDescriptorSize;
public CommonHeader(Stream reader)
{
Version = reader.ReadUInt32();
VolumeInfo = reader.ReadUInt32();
CabDescriptorOffset = (long)reader.ReadUInt32();
CabDescriptorSize = reader.ReadUInt32();
}
}
struct CabDescriptor
{
public readonly long FileTableOffset;
public readonly uint FileTableSize;
public readonly uint FileTableSize2;
public readonly uint DirectoryCount;
public readonly uint FileCount;
public readonly long FileTableOffset2;
public CabDescriptor(Stream reader, CommonHeader commonHeader)
{
reader.Seek(commonHeader.CabDescriptorOffset + 12, SeekOrigin.Begin);
FileTableOffset = (long)reader.ReadUInt32();
/* unknown */ reader.ReadUInt32();
FileTableSize = reader.ReadUInt32();
FileTableSize2 = reader.ReadUInt32();
DirectoryCount = reader.ReadUInt32();
/* unknown */ reader.ReadBytes(8);
FileCount = reader.ReadUInt32();
FileTableOffset2 = (long)reader.ReadUInt32();
}
}
struct FileDescriptor
{
public readonly ushort Flags;
public readonly uint ExpandedSize;
public readonly uint CompressedSize;
public readonly uint DataOffset;
public readonly byte[] MD5;
public readonly uint NameOffset;
public readonly ushort DirectoryIndex;
public readonly uint LinkToPrevious;
public readonly uint LinkToNext;
public readonly byte LinkFlags;
public readonly ushort Volume;
public readonly string Filename;
public FileDescriptor(Stream reader, long tableOffset)
{
Flags = reader.ReadUInt16();
ExpandedSize = reader.ReadUInt32();
/* unknown */ reader.ReadUInt32();
CompressedSize = reader.ReadUInt32();
/* unknown */ reader.ReadUInt32();
DataOffset = reader.ReadUInt32();
/* unknown */ reader.ReadUInt32();
MD5 = reader.ReadBytes(16);
/* unknown */ reader.ReadBytes(16);
NameOffset = reader.ReadUInt32();
DirectoryIndex = reader.ReadUInt16();
/* unknown */ reader.ReadBytes(12);
LinkToPrevious = reader.ReadUInt32();
LinkToNext = reader.ReadUInt32();
LinkFlags = reader.ReadBytes(1)[0];
Volume = reader.ReadUInt16();
var posSave = reader.Position;
reader.Seek(tableOffset + NameOffset, SeekOrigin.Begin);
Filename = reader.ReadASCIIZ();
reader.Seek(posSave, SeekOrigin.Begin);
}
}
class CabReader : IDisposable
{
readonly FileDescriptor fileDes;
public uint RemainingArchiveStream;
public uint RemainingFileStream;
readonly uint index;
readonly string commonName;
ushort volumeNumber;
Stream cabFile;
public CabReader(FileDescriptor fileDes, uint index, string commonName)
{
this.fileDes = fileDes;
this.index = index;
this.commonName = commonName;
volumeNumber = (ushort)((uint)fileDes.Volume - 1u);
RemainingArchiveStream = 0;
if ((fileDes.Flags & FileCompressed) > 0)
RemainingFileStream = fileDes.CompressedSize;
else
RemainingFileStream = fileDes.ExpandedSize;
cabFile = null;
NextFile();
}
public void CopyTo(Stream dest)
{
if ((fileDes.Flags & FileCompressed) != 0)
{
var inf = new Inflater(true);
var buffer = new byte[165535];
do
{
var bytesToExtract = cabFile.ReadUInt16();
RemainingArchiveStream -= 2u;
RemainingFileStream -= 2u;
inf.SetInput(GetBytes(bytesToExtract));
RemainingFileStream -= bytesToExtract;
while (!inf.IsNeedingInput)
{
var inflated = inf.Inflate(buffer);
dest.Write(buffer, 0, inflated);
}
inf.Reset();
}
while (RemainingFileStream > 0);
}
else
{
do
{
RemainingFileStream -= RemainingArchiveStream;
dest.Write(GetBytes(RemainingArchiveStream), 0, (int)RemainingArchiveStream);
}
while (RemainingFileStream > 0);
}
}
public byte[] GetBytes(uint count)
{
if (count < RemainingArchiveStream)
{
RemainingArchiveStream -= count;
return cabFile.ReadBytes((int)count);
}
else
{
var outArray = new byte[count];
var read = cabFile.Read(outArray, 0, (int)RemainingArchiveStream);
if (RemainingFileStream > RemainingArchiveStream)
{
NextFile();
RemainingArchiveStream -= (uint)cabFile.Read(outArray, read, (int)count - read);
}
return outArray;
}
}
public void Dispose()
{
cabFile.Dispose();
}
public void NextFile()
{
if (cabFile != null)
cabFile.Dispose();
++volumeNumber;
cabFile = GlobalFileSystem.Open("{0}{1}.cab".F(commonName, volumeNumber));
if (cabFile.ReadUInt32() != 0x28635349)
throw new InvalidDataException("Not an Installshield CAB package");
uint fileOffset;
if ((fileDes.Flags & FileSplit) != 0)
{
cabFile.Seek(CommonHeader.Size, SeekOrigin.Current);
var head = new VolumeHeader(cabFile);
if (index == head.LastFileIndex)
{
if ((fileDes.Flags & FileCompressed) != 0)
RemainingArchiveStream = head.LastFileSizeCompressed;
else
RemainingArchiveStream = head.LastFileSizeExpanded;
fileOffset = head.LastFileOffset;
}
else if (index == head.FirstFileIndex)
{
if ((fileDes.Flags & FileCompressed) != 0)
RemainingArchiveStream = head.FirstFileSizeCompressed;
else
RemainingArchiveStream = head.FirstFileSizeExpanded;
fileOffset = head.FirstFileOffset;
}
else
throw new Exception("Cannot Resolve Remaining Stream");
}
else
{
if ((fileDes.Flags & FileCompressed) != 0)
RemainingArchiveStream = fileDes.CompressedSize;
else
RemainingArchiveStream = fileDes.ExpandedSize;
fileOffset = fileDes.DataOffset;
}
cabFile.Seek(fileOffset, SeekOrigin.Begin);
}
}
#endregion
readonly Stream hdrFile;
readonly CommonHeader commonHeader;
readonly CabDescriptor cabDescriptor;
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> fileLookup = new Dictionary<string, uint>();
int priority;
string commonName;
public int Priority { get { return priority; } }
public string Name { get { return commonName; } }
public InstallShieldCABExtractor(string hdrFilename, int priority = -1)
{
var fileGroups = new List<FileGroup>();
var fileGroupOffsets = new List<uint>();
this.priority = priority;
hdrFile = GlobalFileSystem.Open(hdrFilename);
// Strips archive number AND file extension
commonName = Regex.Replace(hdrFilename, @"\d*\.[^\.]*$", "");
var signature = hdrFile.ReadUInt32();
if (signature != 0x28635349)
throw new InvalidDataException("Not an Installshield CAB package");
commonHeader = new CommonHeader(hdrFile);
cabDescriptor = new CabDescriptor(hdrFile, commonHeader);
/* unknown */ hdrFile.ReadBytes(14);
for (var i = 0U; i < MaxFileGroupCount; ++i)
fileGroupOffsets.Add(hdrFile.ReadUInt32());
hdrFile.Seek(commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset, SeekOrigin.Begin);
directoryTable = new List<uint>();
for (var i = 0U; i < cabDescriptor.DirectoryCount; ++i)
directoryTable.Add(hdrFile.ReadUInt32());
foreach (var offset in fileGroupOffsets)
{
var nextOffset = offset;
while (nextOffset != 0)
{
hdrFile.Seek((long)nextOffset + 4 + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
var descriptorOffset = hdrFile.ReadUInt32();
nextOffset = hdrFile.ReadUInt32();
hdrFile.Seek((long)descriptorOffset + commonHeader.CabDescriptorOffset, SeekOrigin.Begin);
fileGroups.Add(new FileGroup(hdrFile, commonHeader.CabDescriptorOffset));
}
}
hdrFile.Seek(commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset + cabDescriptor.FileTableOffset2, SeekOrigin.Begin);
foreach (var fileGroup in fileGroups)
{
for (var index = fileGroup.FirstFile; index <= fileGroup.LastFile; ++index)
{
AddFileDescriptorToList(index);
var fileDescriptor = fileDescriptors[index];
var fullFilePath = "{0}\\{1}\\{2}".F(fileGroup.Name, DirectoryName((uint)fileDescriptor.DirectoryIndex), fileDescriptor.Filename);
fileLookup.Add(fullFilePath, index);
}
}
}
public string DirectoryName(uint index)
{
if (directoryNames.ContainsKey(index))
return directoryNames[index];
hdrFile.Seek(commonHeader.CabDescriptorOffset +
cabDescriptor.FileTableOffset +
directoryTable[(int)index],
SeekOrigin.Begin);
var test = hdrFile.ReadASCIIZ();
return test;
}
public bool Exists(string filename)
{
return fileLookup.ContainsKey(filename);
}
public uint DirectoryCount()
{
return cabDescriptor.DirectoryCount;
}
public string FileName(uint index)
{
if (!fileDescriptors.ContainsKey(index))
AddFileDescriptorToList(index);
return fileDescriptors[index].Filename;
}
void AddFileDescriptorToList(uint index)
{
hdrFile.Seek(commonHeader.CabDescriptorOffset +
cabDescriptor.FileTableOffset +
cabDescriptor.FileTableOffset2 +
index * 0x57,
SeekOrigin.Begin);
var fd = new FileDescriptor(hdrFile,
commonHeader.CabDescriptorOffset + cabDescriptor.FileTableOffset);
fileDescriptors.Add(index, fd);
}
public uint FileCount()
{
return cabDescriptor.FileCount;
}
public void ExtractFile(uint index, string fileName)
{
Directory.CreateDirectory(Path.GetDirectoryName(fileName));
using (var destfile = File.Open(fileName, FileMode.Create))
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];
if ((fileDes.Flags & FileInvalid) != 0)
throw new Exception("File Invalid");
if ((fileDes.LinkFlags & LinkPrev) != 0)
return GetContentById(fileDes.LinkToPrevious);
if ((fileDes.Flags & FileObfuscated) != 0)
throw new NotImplementedException("Haven't implemented obfustcated files");
var output = new MemoryStream((int)fileDes.ExpandedSize);
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));
output.Position = 0;
return output;
}
public void GetContentById(uint index, Stream output)
{
var fileDes = fileDescriptors[index];
if ((fileDes.Flags & FileInvalid) != 0)
throw new Exception("File Invalid");
if ((fileDes.LinkFlags & LinkPrev) != 0)
{
GetContentById(fileDes.LinkToPrevious, output);
return;
}
if ((fileDes.Flags & FileObfuscated) != 0)
throw new NotImplementedException("Haven't implemented obfustcated files");
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 GetContent(string fileName)
{
return GetContentById(fileLookup[fileName]);
}
public IEnumerable<uint> CrcHashes()
{
yield break;
}
public IEnumerable<string> AllFileNames()
{
return fileLookup.Keys;
}
public void Dispose()
{
hdrFile.Dispose();
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
{
public 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,46 +71,84 @@ 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);
var data = s.ReadBytes((int)e.Length);
var ret = new MemoryStream();
Blast.Decompress(s, ret);
ret.Seek(0, SeekOrigin.Begin);
return ret;
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 IReadOnlyDictionary<string, Entry> Index { get { return new ReadOnlyDictionary<string, Entry>(index); } }
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-2017 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,63 +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)
{
using (var content = GetContent(kv.Value))
{
var db = new XccLocalDatabase(content);
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"))
{
using (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);
@@ -191,48 +156,130 @@ namespace OpenRA.FileSystem
return ret;
}
public Stream GetContent(PackageEntry entry)
uint? FindMatchingHash(string filename)
{
Stream parentStream;
var baseOffset = dataStart + entry.Offset;
var nestedOffset = baseOffset + SegmentStream.GetOverallNestedOffset(s, out parentStream);
var hash = PackageEntry.HashFilename(filename, type);
if (index.ContainsKey(hash))
return hash;
// Special case FileStream - instead of creating an in-memory copy,
// just reference the portion of the on-disk file that we need to save memory.
// We use GetType instead of 'is' here since we can't handle any derived classes of FileStream.
if (parentStream.GetType() == typeof(FileStream))
{
var path = ((FileStream)parentStream).Name;
return new SegmentStream(File.OpenRead(path), nestedOffset, entry.Length);
}
// For all other streams, create a copy in memory.
// This uses more memory but is the only way in general to ensure the returned streams won't clash.
s.Seek(baseOffset, SeekOrigin.Begin);
return new MemoryStream(s.ReadBytes((int)entry.Length));
}
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 IReadOnlyDictionary<string, PackageEntry> Index
public Stream GetContent(uint hash)
{
get
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 absoluteIndex = index.ToDictionary(e => e.Key, e => new PackageEntry(e.Value.Hash, (uint)(e.Value.Offset + dataStart), e.Value.Length));
return new ReadOnlyDictionary<string, PackageEntry>(absoluteIndex);
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 Contains(string filename)
public bool Exists(string filename)
{
return index.ContainsKey(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-2017 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-2017 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,84 +1,62 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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;
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 ZipFile : IReadWritePackage
public sealed class ZipFile : IFolder
{
public IReadWritePackage Parent { get; private set; }
public string Name { get; private set; }
readonly Stream pkgStream;
readonly SZipFile pkg;
readonly string filename;
readonly int priority;
SZipFile pkg;
static ZipFile()
{
ZipConstants.DefaultCodePage = Encoding.UTF8.CodePage;
ZipConstants.DefaultCodePage = Encoding.Default.CodePage;
}
public ZipFile(Stream stream, string name, IReadOnlyPackage parent = null)
public ZipFile(string filename, int priority)
{
// SharpZipLib breaks when asked to update archives loaded from outside streams or files
// We can work around this by creating a clean in-memory-only file, cutting all outside references
pkgStream = new MemoryStream();
stream.CopyTo(pkgStream);
pkgStream.Position = 0;
Name = name;
Parent = parent as IReadWritePackage;
pkg = new SZipFile(pkgStream);
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)
// Create a new zip with the specified contents
public ZipFile(string filename, int priority, Dictionary<string, byte[]> contents)
{
string name;
IReadOnlyPackage p;
if (!context.TryGetPackageContaining(filename, out p, out name))
throw new FileNotFoundException("Unable to find parent package for " + filename);
this.priority = priority;
this.filename = filename;
Name = name;
Parent = p as IReadWritePackage;
if (File.Exists(filename))
File.Delete(filename);
// SharpZipLib breaks when asked to update archives loaded from outside streams or files
// We can work around this by creating a clean in-memory-only file, cutting all outside references
pkgStream = new MemoryStream();
using (var sourceStream = p.GetStream(name))
sourceStream.CopyTo(pkgStream);
pkgStream.Position = 0;
pkg = new SZipFile(pkgStream);
pkg = SZipFile.Create(filename);
Write(contents);
}
ZipFile(string filename, IReadWritePackage parent)
{
pkgStream = new MemoryStream();
Name = filename;
Parent = parent;
pkg = SZipFile.Create(pkgStream);
}
public static ZipFile Create(string filename, IReadWritePackage parent)
{
return new ZipFile(filename, parent);
}
public Stream GetStream(string filename)
public Stream GetContent(string filename)
{
var entry = pkg.GetEntry(filename);
if (entry == null)
@@ -93,68 +71,64 @@ 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;
}
void Commit()
{
if (Parent == null)
throw new InvalidDataException("Cannot update ZipFile without writable parent");
public int Priority { get { return 500 + priority; } }
public string Name { get { return filename; } }
var pos = pkgStream.Position;
pkgStream.Position = 0;
Parent.Update(Name, pkgStream.ReadBytes((int)pkgStream.Length));
pkgStream.Position = pos;
}
public void Update(string filename, byte[] contents)
public void Write(Dictionary<string, byte[]> contents)
{
// TODO: Clear existing content?
pkg.Close();
pkg = SZipFile.Create(filename);
pkg.BeginUpdate();
pkg.Add(new StaticStreamDataSource(new MemoryStream(contents)), filename);
pkg.CommitUpdate();
Commit();
}
public void Delete(string filename)
{
pkg.BeginUpdate();
pkg.Delete(filename);
foreach (var kvp in contents)
pkg.Add(new StaticMemoryDataSource(kvp.Value), kvp.Key);
pkg.CommitUpdate();
Commit();
pkg.Close();
pkg = new SZipFile(new MemoryStream(File.ReadAllBytes(filename)));
}
public void Dispose()
{
if (pkg != null)
pkg.Close();
if (pkgStream != null)
pkgStream.Dispose();
}
}
class StaticStreamDataSource : IStaticDataSource
class StaticMemoryDataSource : IStaticDataSource
{
readonly Stream s;
public StaticStreamDataSource(Stream s)
byte[] data;
public StaticMemoryDataSource(byte[] data)
{
this.s = s;
this.data = data;
}
public Stream GetSource()
{
return s;
return new MemoryStream(data);
}
}
}

View File

@@ -1,76 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.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,27 +1,23 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using OpenRA.Chat;
using OpenRA.FileSystem;
using OpenRA.Graphics;
using OpenRA.Network;
using OpenRA.Primitives;
@@ -36,9 +32,6 @@ namespace OpenRA
public const int Timestep = 40;
public const int TimestepJankThreshold = 250; // Don't catch up for delays larger than 250ms
public static InstalledMods Mods { get; private set; }
public static ExternalMods ExternalMods { get; private set; }
public static ModData ModData;
public static Settings Settings;
public static ICursor Cursor;
@@ -53,27 +46,22 @@ namespace OpenRA
public static Sound Sound;
public static bool HasInputFocus = false;
public static bool BenchmarkMode = false;
public static GlobalChat GlobalChat;
static Task discoverNat;
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(() => { return TimestampedFilename(); });
connection = new ReplayRecorderConnection(connection, TimestampedFilename);
var om = new OrderManager(host, port, password, connection);
JoinInner(om);
return om;
}
static string TimestampedFilename(bool includemilliseconds = false)
static string TimestampedFilename()
{
var format = includemilliseconds ? "yyyy-MM-ddTHHmmssfffZ" : "yyyy-MM-ddTHHmmssZ";
return "OpenRA-" + DateTime.UtcNow.ToString(format, CultureInfo.InvariantCulture);
return DateTime.UtcNow.ToString("OpenRA-yyyy-MM-ddTHHmmssZ");
}
static void JoinInner(OrderManager om)
@@ -96,7 +84,7 @@ namespace OpenRA
// More accurate replacement for Environment.TickCount
static Stopwatch stopwatch = Stopwatch.StartNew();
public static long RunTime { get { return 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; } }
@@ -165,7 +153,7 @@ namespace OpenRA
using (new PerfTimer("NewWorld"))
OrderManager.World = new World(map, OrderManager, type);
worldRenderer = new WorldRenderer(ModData, OrderManager.World);
worldRenderer = new WorldRenderer(OrderManager.World);
using (new PerfTimer("LoadComplete"))
OrderManager.World.LoadComplete(worldRenderer);
@@ -185,44 +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)
{
OrderManager om = null;
Action lobbyReady = null;
lobbyReady = () =>
{
LobbyInfoChanged -= lobbyReady;
foreach (var o in setupOrders)
om.IssueOrder(o);
};
LobbyInfoChanged += lobbyReady;
om = JoinServer(IPAddress.Loopback.ToString(), CreateLocalServer(mapUID), "");
}
public static bool IsHost
{
get
@@ -239,110 +189,68 @@ 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);
// Special case handling of Game.Mod argument: if it matches a real filesystem path
// then we use this to override the mod search path, and replace it with the mod id
var modArgument = args.GetValue("Game.Mod", null);
var explicitModPaths = new string[0];
if (modArgument != null && (File.Exists(modArgument) || Directory.Exists(modArgument)))
{
explicitModPaths = new[] { modArgument };
args.ReplaceValue("Game.Mod", Path.GetFileNameWithoutExtension(modArgument));
}
AppDomain.CurrentDomain.AssemblyResolve += GlobalFileSystem.ResolveAssembly;
InitializeSettings(args);
Log.AddChannel("perf", "perf.log");
Log.AddChannel("debug", "debug.log");
Log.AddChannel("sync", "syncreport.log");
Log.AddChannel("server", "server.log");
Log.AddChannel("sound", "sound.log");
Log.AddChannel("graphics", "graphics.log");
Log.AddChannel("geoip", "geoip.log");
Log.AddChannel("irc", "irc.log");
Log.AddChannel("nat", "nat.log");
var platforms = new[] { Settings.Game.Platform, "Default", null };
foreach (var p in platforms)
if (Settings.Server.DiscoverNatDevices)
UPnP.TryNatDiscovery();
else
{
if (p == null)
throw new InvalidOperationException("Failed to initialize platform-integration library. Check graphics.log for details.");
Settings.Server.NatDeviceAvailable = false;
Settings.Server.AllowPortForward = false;
}
Settings.Game.Platform = p;
GeoIP.Initialize();
GlobalFileSystem.Mount(Platform.GameDir); // Needed to access shaders
var renderers = new[] { Settings.Graphics.Renderer, "Default", null };
foreach (var r in renderers)
{
if (r == null)
throw new InvalidOperationException("No suitable renderers were found. Check graphics.log for details.");
Settings.Graphics.Renderer = r;
try
{
var rendererPath = Platform.ResolvePath(Path.Combine(".", "OpenRA.Platforms." + p + ".dll"));
var assembly = Assembly.LoadFile(rendererPath);
var platformType = assembly.GetTypes().SingleOrDefault(t => typeof(IPlatform).IsAssignableFrom(t));
if (platformType == null)
throw new InvalidOperationException("Platform dll must include exactly one IPlatform implementation.");
var platform = (IPlatform)platformType.GetConstructor(Type.EmptyTypes).Invoke(null);
Renderer = new Renderer(platform, Settings.Graphics);
Sound = new Sound(platform, Settings.Sound);
Renderer = new Renderer(Settings.Graphics, Settings.Server);
break;
}
catch (Exception e)
{
Log.Write("graphics", "{0}", e);
Console.WriteLine("Renderer initialization failed. Check graphics.log for details.");
if (Renderer != null)
Renderer.Dispose();
if (Sound != null)
Sound.Dispose();
Console.WriteLine("Renderer initialization failed. Fallback in place. Check graphics.log for details.");
}
}
GeoIP.Initialize();
if (!Settings.Server.DiscoverNatDevices)
Settings.Server.AllowPortForward = false;
else
{
discoverNat = UPnP.DiscoverNatDevices(Settings.Server.NatDiscoveryTimeout);
Settings.Server.AllowPortForward = true;
}
Sound = new Sound(Settings.Server.Dedicated ? "Null" : Settings.Sound.Engine);
GlobalChat = new GlobalChat();
var modSearchArg = args.GetValue("Engine.ModSearchPaths", null);
var modSearchPaths = modSearchArg != null ?
FieldLoader.GetValue<string[]>("Engine.ModsPath", modSearchArg) :
new[] { Path.Combine(".", "mods"), Path.Combine("^", "mods") };
Mods = new InstalledMods(modSearchPaths, explicitModPaths);
Console.WriteLine("Internal mods:");
foreach (var mod in Mods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Metadata.Title, mod.Value.Metadata.Version);
var launchPath = args.GetValue("Engine.LaunchPath", Assembly.GetEntryAssembly().Location);
ExternalMods = new ExternalMods(launchPath);
Console.WriteLine("External mods:");
foreach (var mod in ExternalMods)
Console.WriteLine("Available mods:");
foreach (var mod in ModMetadata.AllMods)
Console.WriteLine("\t{0}: {1} ({2})", mod.Key, mod.Value.Title, mod.Value.Version);
InitializeMod(Settings.Game.Mod, args);
}
public static bool IsModInstalled(string modId)
{
return Mods.ContainsKey(modId) && Mods[modId].RequiresMods.All(IsModInstalled);
}
public static bool IsModInstalled(KeyValuePair<string, string> mod)
{
return Mods.ContainsKey(mod.Key)
&& Mods[mod.Key].Metadata.Version == mod.Value
&& IsModInstalled(mod.Key);
if (Settings.Server.DiscoverNatDevices)
RunAfterDelay(Settings.Server.NatDiscoveryTimeout, UPnP.StoppingNatDiscovery);
}
public static void InitializeMod(string mod, Arguments args)
@@ -365,36 +273,43 @@ 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 (mod == null || !IsModInstalled(mod))
mod = args.GetValue("Engine.DefaultMod", "modchooser");
// 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(Mods[mod], Mods, true);
ExternalMods.Register(ModData.Manifest);
if (!ModData.LoadScreen.BeforeLoad())
return;
ModData = new ModData(mod, !Settings.Server.Dedicated);
using (new PerfTimer("LoadMaps"))
ModData.MapCache.LoadMaps();
ModData.InitializeLoaders(ModData.DefaultFileSystem);
Renderer.InitializeFonts(ModData);
if (Settings.Server.Dedicated)
{
RunDedicatedServer();
Environment.Exit(0);
}
var grid = ModData.Manifest.Contains<MapGrid>() ? ModData.Manifest.Get<MapGrid>() : null;
Renderer.InitializeDepthBuffer(grid);
var installData = ModData.Manifest.Get<ContentInstaller>();
var isModContentInstalled = installData.TestFiles.All(f => File.Exists(Platform.ResolvePath(f)));
// Mod assets are missing!
if (!isModContentInstalled)
{
InitializeMod("modchooser", new Arguments());
return;
}
ModData.MountFiles();
ModData.InitializeLoaders();
Renderer.InitializeFonts(ModData.Manifest);
if (Cursor != null)
Cursor.Dispose();
@@ -426,21 +341,40 @@ namespace OpenRA
JoinLocal();
try
{
if (discoverNat != null)
discoverNat.Wait();
}
catch (Exception e)
{
Console.WriteLine("NAT discovery failed: {0}", e.Message);
Log.Write("nat", e.ToString());
Settings.Server.AllowPortForward = false;
}
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);
@@ -457,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())
@@ -466,36 +400,14 @@ namespace OpenRA
return shellmaps.Random(CosmeticRandom);
}
public static void SwitchToExternalMod(ExternalMod mod, string[] launchArguments = null, Action onFailed = null)
{
try
{
var args = launchArguments != null ? mod.LaunchArgs.Append(launchArguments) : mod.LaunchArgs;
var p = Process.Start(mod.LaunchPath, args.Select(a => "\"" + a + "\"").JoinWith(" "));
if (p == null || p.HasExited)
onFailed();
else
{
p.Close();
Exit();
}
}
catch (Exception e)
{
Log.Write("debug", "Failed to switch to external mod.");
Log.Write("debug", "Error was: " + e.Message);
onFailed();
}
}
static RunStatus state = RunStatus.Running;
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()
{
@@ -507,11 +419,11 @@ namespace OpenRA
ThreadPool.QueueUserWorkItem(_ =>
{
var mod = ModData.Manifest.Metadata;
var directory = Platform.ResolvePath("^", "Screenshots", ModData.Manifest.Id, mod.Version);
var mod = ModData.Manifest.Mod;
var directory = Platform.ResolvePath("^", "Screenshots", mod.Id, mod.Version);
Directory.CreateDirectory(directory);
var filename = TimestampedFilename(true);
var filename = TimestampedFilename();
var format = Settings.Graphics.ScreenshotFormat;
var extension = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == format.Guid)
.FilenameExtension.Split(';').First().ToLowerInvariant().Substring(1);
@@ -522,7 +434,7 @@ namespace OpenRA
bitmap.Dispose();
RunAfterTick(() => Debug("Saved screenshot " + filename));
Game.RunAfterTick(() => Debug("Saved screenshot " + filename));
});
}
@@ -561,46 +473,42 @@ namespace OpenRA
Sound.Tick();
Sync.CheckSyncUnchanged(world, orderManager.TickImmediate);
if (world == null)
return;
var isNetTick = LocalTick % NetTickScale == 0;
if (!isNetTick || orderManager.IsReadyForNextFrame)
if (world != null)
{
++orderManager.LocalFrameNumber;
var isNetTick = LocalTick % NetTickScale == 0;
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();
Sync.CheckSyncUnchanged(world, () =>
if (!isNetTick || orderManager.IsReadyForNextFrame)
{
world.OrderGenerator.Tick(world);
world.Selection.Tick(world);
});
++orderManager.LocalFrameNumber;
world.Tick();
Log.Write("debug", "--Tick: {0} ({1})", LocalTick, isNetTick ? "net" : "local");
PerfHistory.Tick();
}
else if (orderManager.NetFrameNumber == 0)
orderManager.LastTickTime = RunTime;
if (isNetTick)
orderManager.Tick();
Sync.CheckSyncUnchanged(world, () =>
{
world.OrderGenerator.Tick(world);
world.Selection.Tick(world);
});
world.Tick();
PerfHistory.Tick();
}
else
if (orderManager.NetFrameNumber == 0)
orderManager.LastTickTime = RunTime;
// Wait until we have done our first world Tick before TickRendering
if (orderManager.LocalFrameNumber > 0)
Sync.CheckSyncUnchanged(world, () => world.TickRender(worldRenderer));
}
}
}
}
static void LogicTick()
{
delayedActions.PerformActions(RunTime);
delayedActions.PerformActions(Game.RunTime);
if (OrderManager.Connection.ConnectionState != lastConnectionState)
{
@@ -633,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();
@@ -660,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()
@@ -713,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;
@@ -761,7 +666,7 @@ namespace OpenRA
}
}
else
Thread.Sleep((int)(nextUpdate - now));
Thread.Sleep(nextUpdate - now);
}
}
@@ -803,6 +708,11 @@ namespace OpenRA
state = RunStatus.Success;
}
public static void Restart()
{
state = RunStatus.Restart;
}
public static void AddChatLine(Color color, string name, string text)
{
OrderManager.AddChatLine(color, name, text);
@@ -836,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)
@@ -849,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-2017 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-2017 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-2017 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,29 +34,18 @@ 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;
try
Exists = true;
using (var s = GlobalFileSystem.Open(Filename))
{
Exists = true;
foreach (var loader in Game.ModData.SoundLoaders)
{
ISoundFormat soundFormat;
if (loader.TryParseSound(stream, out soundFormat))
{
Length = (int)soundFormat.LengthInSeconds;
soundFormat.Dispose();
break;
}
}
}
finally
{
stream.Dispose();
if (Filename.ToLowerInvariant().EndsWith("wav"))
Length = (int)WavLoader.WaveLength(s);
else
Length = (int)AudLoader.SoundLength(s);
}
}
}

View File

@@ -1,19 +1,15 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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), 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-2017 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-2017 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,14 +24,12 @@ 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;
}
public interface IProjectile : IEffect { }
public interface IProjectileInfo { IProjectile Create(ProjectileArgs args); }
public interface IProjectileInfo { IEffect Create(ProjectileArgs args); }
public sealed class WeaponInfo
{
@@ -48,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" };
@@ -68,9 +67,6 @@ namespace OpenRA.GameRules
public WeaponInfo(string name, MiniYaml content)
{
// Resolve any weapon-level yaml inheritance or removals
// HACK: The "Defaults" sequence syntax prevents us from doing this generally during yaml parsing
content.Nodes = MiniYaml.Merge(new[] { content.Nodes });
FieldLoader.Load(this, content);
}
@@ -130,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>
@@ -162,10 +155,11 @@ namespace OpenRA.GameRules
{
var wh = warhead; // force the closure to bind to the current warhead
Action a = () => wh.DoImpact(target, firedBy, damageModifiers);
if (wh.Delay > 0)
firedBy.World.AddFrameEndTask(w => w.Add(new DelayedImpact(wh.Delay, wh, target, firedBy, damageModifiers)));
firedBy.World.AddFrameEndTask(w => w.Add(new DelayedAction(wh.Delay, a)));
else
wh.DoImpact(target, firedBy, damageModifiers);
a();
}
}
}

View File

@@ -1,15 +1,17 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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
#region Copyright & License Information
/*
* Copyright 2007-2017 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-2017 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-2017 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-2017 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), 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-2017 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), 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-2017 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-2017 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

@@ -1,22 +1,23 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.
*/
* 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;
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-2017 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-2017 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,23 @@ using OpenRA.Graphics;
namespace OpenRA
{
public interface IPlatform
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class PlatformAttribute : Attribute
{
public readonly Type Type;
public PlatformAttribute(Type graphicsDeviceType)
{
if (!typeof(IDeviceFactory).IsAssignableFrom(graphicsDeviceType))
throw new InvalidOperationException("Incorrect type in RendererAttribute");
Type = graphicsDeviceType;
}
}
public interface IDeviceFactory
{
IGraphicsDevice CreateGraphics(Size size, WindowMode windowMode);
ISoundEngine CreateSound(string device);
ISoundEngine CreateSound();
}
public interface IHardwareCursor : IDisposable { }
@@ -43,23 +55,20 @@ namespace OpenRA
IShader CreateShader(string name);
Size WindowSize { get; }
float WindowScale { get; }
event Action<float, float> OnWindowScaleChanged;
void Clear();
void Present();
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();
void EnableDepthBuffer();
void DisableDepthBuffer();
void ClearDepthBuffer();
void SetBlendMode(BlendMode mode);
@@ -68,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
@@ -85,7 +92,6 @@ namespace OpenRA
void SetBool(string name, bool value);
void SetVec(string name, float x);
void SetVec(string name, float x, float y);
void SetVec(string name, float x, float y, float z);
void SetVec(string name, float[] vec, int length);
void SetTexture(string param, ITexture texture);
void SetMatrix(string param, float[] mtx);
@@ -116,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-2017 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-2017 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-2017 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-2017 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-2017 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,295 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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(float3 start, float3 end, float width, Color startColor, Color endColor)
{
renderer.CurrentBatchRenderer = this;
if (nv + 6 > renderer.TempBufferSize)
Flush();
var delta = (end - start) / (end - start).XY.Length;
var corner = width / 2 * new float3(-delta.Y, delta.X, delta.Z);
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, 0, 0);
vertices[nv++] = new Vertex(start + corner + Offset, sr, sg, sb, sa, 0, 0);
vertices[nv++] = new Vertex(end + corner + Offset, er, eg, eb, ea, 0, 0);
vertices[nv++] = new Vertex(end + corner + Offset, er, eg, eb, ea, 0, 0);
vertices[nv++] = new Vertex(end - corner + Offset, er, eg, eb, ea, 0, 0);
vertices[nv++] = new Vertex(start - corner + Offset, sr, sg, sb, sa, 0, 0);
}
public void DrawLine(float3 start, float3 end, float width, Color color)
{
renderer.CurrentBatchRenderer = this;
if (nv + 6 > renderer.TempBufferSize)
Flush();
var delta = (end - start) / (end - start).XY.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, 0, 0);
vertices[nv++] = new Vertex(start + corner + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(end + corner + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(end + corner + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(end - corner + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(start - corner + Offset, r, g, b, a, 0, 0);
}
/// <summary>
/// Calculate the 2D intersection of two lines.
/// Will behave badly if the lines are parallel.
/// Z position is the average of a and b (ignores actual intersection point if it exists)
/// </summary>
float3 IntersectionOf(float3 a, float3 da, float3 b, float3 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 float3(x / d, y / d, 0.5f * (a.Z + b.Z));
}
void DrawDisconnectedLine(IEnumerable<float3> 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(float3[] 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).XY.Length;
var corner = width / 2 * new float3(-dir.Y, dir.X, dir.Z);
// 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).XY.Length;
var prevCorner = width / 2 * new float3(-prevDir.Y, prevDir.X, prevDir.Z);
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).XY.Length;
var nextCorner = width / 2 * new float3(-nextDir.Y, nextDir.X, nextDir.Z);
// 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, 0, 0);
vertices[nv++] = new Vertex(cb + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(cc + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(cd + Offset, r, g, b, a, 0, 0);
vertices[nv++] = new Vertex(ca + Offset, r, g, b, a, 0, 0);
// 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)
{
DrawLine(points.Select(p => new float3(p, 0)), width, color, connectSegments);
}
public void DrawLine(IEnumerable<float3> points, float width, Color color, bool connectSegments = false)
{
if (!connectSegments)
DrawDisconnectedLine(points, width, color);
else
DrawConnectedLine(points as float3[] ?? points.ToArray(), width, color, false);
}
public void DrawPolygon(float3[] vertices, float width, Color color)
{
DrawConnectedLine(vertices, width, color, true);
}
public void DrawPolygon(float2[] vertices, float width, Color color)
{
DrawConnectedLine(vertices.Select(v => new float3(v, 0)).ToArray(), width, color, true);
}
public void DrawRect(float3 tl, float3 br, float width, Color color)
{
var tr = new float3(br.X, tl.Y, tl.Z);
var bl = new float3(tl.X, br.Y, br.Z);
DrawPolygon(new[] { tl, tr, br, bl }, width, color);
}
public void FillRect(float3 tl, float3 br, Color color)
{
var tr = new float3(br.X, tl.Y, tl.Z);
var bl = new float3(tl.X, br.Y, br.Z);
FillRect(tl, tr, br, bl, color);
}
public void FillRect(float3 a, float3 b, float3 c, float3 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, 0, 0);
vertices[nv++] = new Vertex(b + Offset, cr, cg, cb, ca, 0, 0);
vertices[nv++] = new Vertex(c + Offset, cr, cg, cb, ca, 0, 0);
vertices[nv++] = new Vertex(c + Offset, cr, cg, cb, ca, 0, 0);
vertices[nv++] = new Vertex(d + Offset, cr, cg, cb, ca, 0, 0);
vertices[nv++] = new Vertex(a + Offset, cr, cg, cb, ca, 0, 0);
}
public void FillEllipse(float3 tl, float3 br, Color color, int vertices = 32)
{
// TODO: Create an ellipse polygon instead
var a = (br.X - tl.X) / 2;
var b = (br.Y - tl.Y) / 2;
var xc = (br.X + tl.X) / 2;
var yc = (br.Y + tl.Y) / 2;
for (var y = tl.Y; y <= br.Y; y++)
{
var z = float2.Lerp(tl.Z, br.Z, (y - tl.Y) / (br.Y - tl.Y));
var dx = a * (float)Math.Sqrt(1 - (y - yc) * (y - yc) / b / b);
DrawLine(new float3(xc - dx, y, z), new float3(xc + dx, y, z), 1, color);
}
}
public void SetViewportParams(Size screen, float depthScale, float depthOffset, float zoom, int2 scroll)
{
shader.SetVec("Scroll", scroll.X, scroll.Y, scroll.Y);
shader.SetVec("r1",
zoom * 2f / screen.Width,
-zoom * 2f / screen.Height,
-depthScale * zoom / screen.Height);
shader.SetVec("r2", -1, 1, 1 - depthOffset);
}
public void SetDepthPreviewEnabled(bool enabled)
{
shader.SetBool("EnableDepthPreview", enabled);
}
}
}

View File

@@ -1,15 +1,15 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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; } }
@@ -48,63 +39,81 @@ namespace OpenRA.Graphics
public IRenderable OffsetBy(WVec vec) { return new SelectionBarsRenderable(pos + vec, actor); }
public IRenderable AsDecoration() { return this; }
void DrawExtraBars(WorldRenderer wr, float3 start, float3 end)
void DrawExtraBars(WorldRenderer wr, float2 start, float2 end)
{
foreach (var extraBar in actor.TraitsImplementing<ISelectionBar>())
{
var value = extraBar.GetValue();
if (value != 0 || extraBar.DisplayWhenEmpty)
if (value != 0)
{
var offset = new float3(0, (int)(4 / wr.Viewport.Zoom), 0);
start += offset;
end += offset;
start.Y += (int)(4 / wr.Viewport.Zoom);
end.Y += (int)(4 / wr.Viewport.Zoom);
DrawSelectionBar(wr, start, end, extraBar.GetValue(), extraBar.GetColor());
}
}
}
void DrawSelectionBar(WorldRenderer wr, float3 start, float3 end, float value, Color barColor)
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 = float3.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 z = float2.Lerp(start, end, value);
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;
}
void DrawHealthBar(WorldRenderer wr, IHealth health, float3 start, float3 end)
void DrawHealthBar(WorldRenderer wr, IHealth health, float2 start, float2 end)
{
if (health == null || health.IsDead)
return;
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(
@@ -113,16 +122,16 @@ namespace OpenRA.Graphics
healthColor.G / 2,
healthColor.B / 2);
var z = float3.Lerp(start, end, (float)health.HP / health.MaxHP);
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)
{
@@ -132,11 +141,11 @@ namespace OpenRA.Graphics
deltaColor.R / 2,
deltaColor.G / 2,
deltaColor.B / 2);
var zz = float3.Lerp(start, end, (float)health.DisplayHP / health.MaxHP);
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);
}
}
@@ -148,18 +157,15 @@ namespace OpenRA.Graphics
var health = actor.TraitOrDefault<IHealth>();
var screenPos = wr.Screen3DPxPosition(pos);
var screenPos = wr.ScreenPxPosition(pos);
var bounds = actor.VisualBounds;
bounds.Offset((int)screenPos.X, (int)screenPos.Y);
bounds.Offset(screenPos.X, screenPos.Y);
var start = new float3(bounds.Left + 1, bounds.Top, screenPos.Z);
var end = new float3(bounds.Right - 1, bounds.Top, screenPos.Z);
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-2017 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-2017 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-2017 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
{
@@ -22,11 +22,10 @@ namespace OpenRA.Graphics
: base(message) { }
}
// The enum values indicate the number of channels used by the type
// They are not arbitrary IDs!
public enum SheetType
{
Indexed = 1,
DualIndexed = 2,
BGRA = 4,
}
@@ -61,15 +60,15 @@ namespace OpenRA.Graphics
this.allocateSheet = allocateSheet;
}
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, 0, frame.Offset); }
public Sprite Add(byte[] src, Size size) { return Add(src, size, 0, float3.Zero); }
public Sprite Add(byte[] src, Size size, float zRamp, float3 spriteOffset)
public Sprite Add(ISpriteFrame frame) { return Add(frame.Data, frame.Size, frame.Offset); }
public Sprite Add(byte[] src, Size size) { return Add(src, size, float2.Zero); }
public Sprite Add(byte[] src, Size size, float2 spriteOffset)
{
// Don't bother allocating empty sprites
if (size.Width == 0 || size.Height == 0)
return new Sprite(current, Rectangle.Empty, 0, spriteOffset, channel, BlendMode.Alpha);
return new Sprite(current, Rectangle.Empty, spriteOffset, channel, BlendMode.Alpha);
var rect = Allocate(size, zRamp, spriteOffset);
var rect = Allocate(size, spriteOffset);
Util.FastCopyIntoChannel(rect, src);
current.CommitBufferedData();
return rect;
@@ -101,8 +100,8 @@ namespace OpenRA.Graphics
return (TextureChannel)nextChannel;
}
public Sprite Allocate(Size imageSize) { return Allocate(imageSize, 0, float3.Zero); }
public Sprite Allocate(Size imageSize, float zRamp, float3 spriteOffset)
public Sprite Allocate(Size imageSize) { return Allocate(imageSize, float2.Zero); }
public Sprite Allocate(Size imageSize, float2 spriteOffset)
{
if (imageSize.Width + p.X > current.Size.Width)
{
@@ -130,7 +129,7 @@ namespace OpenRA.Graphics
p = new Point(0, 0);
}
var rect = new Sprite(current, new Rectangle(p, imageSize), zRamp, spriteOffset, channel, BlendMode.Alpha);
var rect = new Sprite(current, new Rectangle(p, imageSize), spriteOffset, channel, BlendMode.Alpha);
p.X += imageSize.Width;
return rect;

View File

@@ -1,17 +1,18 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.
*/
* 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;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
namespace OpenRA.Graphics
@@ -23,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;
@@ -81,8 +82,8 @@ namespace OpenRA.Graphics
var cursorSize = CursorProvider.CursorViewportZoomed ? 2.0f * cursorSprite.Size : cursorSprite.Size;
var cursorOffset = CursorProvider.CursorViewportZoomed ?
(2 * cursorSequence.Hotspot) + cursorSprite.Size.XY.ToInt2() :
cursorSequence.Hotspot + (0.5f * cursorSprite.Size.XY).ToInt2();
(2 * cursorSequence.Hotspot) + cursorSprite.Size.ToInt2() :
cursorSequence.Hotspot + (0.5f * cursorSprite.Size).ToInt2();
renderer.SetPalette(palette);
renderer.SpriteRenderer.DrawSprite(cursorSprite,

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,26 +19,24 @@ namespace OpenRA.Graphics
public readonly Sheet Sheet;
public readonly BlendMode BlendMode;
public readonly TextureChannel Channel;
public readonly float ZRamp;
public readonly float3 Size;
public readonly float3 Offset;
public readonly float3 FractionalOffset;
public readonly float2 Size;
public readonly float2 Offset;
public readonly float2 FractionalOffset;
public readonly float Top, Left, Bottom, Right;
public Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
: this(sheet, bounds, 0, float2.Zero, channel) { }
: this(sheet, bounds, float2.Zero, channel) { }
public Sprite(Sheet sheet, Rectangle bounds, float zRamp, float3 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha)
public Sprite(Sheet sheet, Rectangle bounds, float2 offset, TextureChannel channel, BlendMode blendMode = BlendMode.Alpha)
{
Sheet = sheet;
Bounds = bounds;
Offset = offset;
ZRamp = zRamp;
Channel = channel;
Size = new float3(bounds.Size.Width, bounds.Size.Height, bounds.Size.Height * zRamp);
Size = new float2(bounds.Size);
BlendMode = blendMode;
FractionalOffset = Size.Z != 0 ? offset / Size :
new float3(offset.X / Size.X, offset.Y / Size.Y, 0);
FractionalOffset = offset / Size;
Left = (float)Math.Min(bounds.Left, bounds.Right) / sheet.Size.Width;
Top = (float)Math.Min(bounds.Top, bounds.Bottom) / sheet.Size.Height;
@@ -48,24 +45,6 @@ namespace OpenRA.Graphics
}
}
public class SpriteWithSecondaryData : Sprite
{
public readonly Rectangle SecondaryBounds;
public readonly TextureChannel SecondaryChannel;
public readonly float SecondaryTop, SecondaryLeft, SecondaryBottom, SecondaryRight;
public SpriteWithSecondaryData(Sprite s, Rectangle secondaryBounds, TextureChannel secondaryChannel)
: base(s.Sheet, s.Bounds, s.ZRamp, s.Offset, s.Channel, s.BlendMode)
{
SecondaryBounds = secondaryBounds;
SecondaryChannel = secondaryChannel;
SecondaryLeft = (float)Math.Min(secondaryBounds.Left, secondaryBounds.Right) / s.Sheet.Size.Width;
SecondaryTop = (float)Math.Min(secondaryBounds.Top, secondaryBounds.Bottom) / s.Sheet.Size.Height;
SecondaryRight = (float)Math.Max(secondaryBounds.Left, secondaryBounds.Right) / s.Sheet.Size.Width;
SecondaryBottom = (float)Math.Max(secondaryBounds.Top, secondaryBounds.Bottom) / s.Sheet.Size.Height;
}
}
public enum TextureChannel : byte
{
Red = 0,

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,12 +13,11 @@ using System.Drawing;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Support;
using OpenRA.Widgets;
using SharpFont;
namespace OpenRA.Graphics
{
public sealed class SpriteFont : IDisposable
public class SpriteFont
{
static readonly Library Library = new Library();
@@ -29,37 +27,26 @@ namespace OpenRA.Graphics
readonly Face face;
readonly Cache<Pair<char, Color>, GlyphInfo> glyphs;
float deviceScale;
public SpriteFont(string name, byte[] data, int size, float scale, 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");
deviceScale = scale;
this.size = size;
this.builder = builder;
face = new Face(Library, data, 0);
face.SetPixelSizes((uint)(size * deviceScale), (uint)(size * deviceScale));
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) / deviceScale;
lineWidth = line => line.Sum(characterWidth);
PrecacheColor(Color.White, name);
PrecacheColor(Color.Red, name);
}
public void SetScale(float scale)
{
deviceScale = scale;
face.SetPixelSizes((uint)(size * deviceScale), (uint)(size * deviceScale));
glyphs.Clear();
}
void PrecacheColor(Color c, string name)
{
using (new PerfTimer("PrecacheColor {0} {1}px {2}".F(name, size, c.Name)))
@@ -85,10 +72,9 @@ namespace OpenRA.Graphics
var g = glyphs[Pair.New(s, c)];
Game.Renderer.RgbaSpriteRenderer.DrawSprite(g.Sprite,
new float2(
(int)Math.Round(p.X * deviceScale + g.Offset.X, 0) / deviceScale,
p.Y + g.Offset.Y / deviceScale),
g.Sprite.Size / deviceScale);
p.X += g.Advance / deviceScale;
(int)Math.Round(p.X + g.Offset.X, 0),
p.Y + g.Offset.Y));
p.X += g.Advance;
}
}
@@ -96,33 +82,15 @@ namespace OpenRA.Graphics
{
if (offset > 0)
{
DrawText(text, location + new float2(-offset / deviceScale, 0), bg);
DrawText(text, location + new float2(offset / deviceScale, 0), bg);
DrawText(text, location + new float2(0, -offset / deviceScale), bg);
DrawText(text, location + new float2(0, offset / deviceScale), bg);
DrawText(text, location + new float2(-offset, 0), bg);
DrawText(text, location + new float2(offset, 0), bg);
DrawText(text, location + new float2(0, -offset), bg);
DrawText(text, location + new float2(0, offset), bg);
}
DrawText(text, location, fg);
}
public void DrawTextWithContrast(string text, float2 location, Color fg, Color bgDark, Color bgLight, int offset)
{
DrawTextWithContrast(text, location, fg, WidgetUtils.GetContrastColor(fg, bgDark, bgLight), offset);
}
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bg, int offset)
{
if (offset != 0)
DrawText(text, location + new float2(offset, offset), bg);
DrawText(text, location, fg);
}
public void DrawTextWithShadow(string text, float2 location, Color fg, Color bgDark, Color bgLight, int offset)
{
DrawTextWithShadow(text, location, fg, WidgetUtils.GetContrastColor(fg, bgDark, bgLight), offset);
}
public int2 Measure(string text)
{
if (string.IsNullOrEmpty(text))
@@ -181,11 +149,6 @@ namespace OpenRA.Graphics
return g;
}
public void Dispose()
{
face.Dispose();
}
}
class GlyphInfo

View File

@@ -1,15 +1,13 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.IO;
using System.Linq;
@@ -25,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; }
@@ -44,56 +33,25 @@ namespace OpenRA.Graphics
public class SpriteCache
{
public readonly SheetBuilder SheetBuilder;
readonly ISpriteLoader[] loaders;
readonly IReadOnlyFileSystem fileSystem;
readonly Cache<string, Sprite[]> sprites;
readonly Dictionary<string, List<Sprite[]>> sprites = new Dictionary<string, List<Sprite[]>>();
public SpriteCache(IReadOnlyFileSystem fileSystem, ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
public SpriteCache(ISpriteLoader[] loaders, SheetBuilder sheetBuilder)
{
SheetBuilder = sheetBuilder;
this.fileSystem = fileSystem;
this.loaders = loaders;
sprites = new Cache<string, Sprite[]>(filename => SpriteLoader.GetSprites(filename, loaders, sheetBuilder));
}
Sprite[] LoadSprite(string filename, List<Sprite[]> cache)
{
var sprite = SpriteLoader.GetSprites(fileSystem, filename, loaders, SheetBuilder);
cache.Add(sprite);
return sprite;
}
/// <summary>Returns the first set of sprites with the given filename.</summary>
public Sprite[] this[string filename]
{
get
{
var allSprites = sprites.GetOrAdd(filename);
var sprite = allSprites.FirstOrDefault();
return sprite ?? LoadSprite(filename, allSprites);
}
}
/// <summary>Returns all instances of sets of sprites with the given filename</summary>
public IEnumerable<Sprite[]> AllCached(string filename)
{
return sprites.GetOrAdd(filename);
}
/// <summary>Loads and caches a new instance of sprites with the given filename</summary>
public Sprite[] Reload(string filename)
{
return LoadSprite(filename, sprites.GetOrAdd(filename));
}
public Sprite[] this[string filename] { get { return sprites[filename]; } }
}
public class FrameCache
{
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]; } }
@@ -101,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-2017 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
@@ -49,30 +48,27 @@ namespace OpenRA.Graphics
public IRenderable OffsetBy(WVec vec) { return new SpriteRenderable(sprite, pos + vec, offset, zOffset, palette, scale, isDecoration); }
public IRenderable AsDecoration() { return new SpriteRenderable(sprite, pos, offset, zOffset, palette, scale, true); }
float3 ScreenPosition(WorldRenderer wr)
float2 ScreenPosition(WorldRenderer wr)
{
var xy = wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size.XY).ToInt2();
// HACK: The z offset needs to be applied somewhere, but this probably is the wrong place.
return new float3(xy, sprite.Offset.Z + wr.ScreenZPosition(pos, 0) - 0.5f * scale * sprite.Size.Z);
return wr.ScreenPxPosition(pos) + wr.ScreenPxOffset(offset) - (0.5f * scale * sprite.Size).ToInt2();
}
public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; }
public void Render(WorldRenderer wr)
{
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, scale * sprite.Size);
Game.Renderer.WorldSpriteRenderer.DrawSprite(sprite, ScreenPosition(wr), palette, sprite.Size * scale);
}
public void RenderDebugGeometry(WorldRenderer wr)
{
var screenOffset = ScreenPosition(wr) + sprite.Offset;
Game.Renderer.WorldRgbaColorRenderer.DrawRect(screenOffset, screenOffset + sprite.Size, 1 / wr.Viewport.Zoom, Color.Red);
var offset = ScreenPosition(wr) + sprite.Offset;
Game.Renderer.WorldLineRenderer.DrawRect(offset, offset + sprite.Size, Color.Red);
}
public Rectangle ScreenBounds(WorldRenderer wr)
{
var screenOffset = ScreenPosition(wr) + sprite.Offset;
return new Rectangle((int)screenOffset.X, (int)screenOffset.Y, (int)sprite.Size.X, (int)sprite.Size.Y);
var offset = ScreenPosition(wr) + sprite.Offset;
return new Rectangle((int)offset.X, (int)offset.Y, (int)sprite.Size.X, (int)sprite.Size.Y);
}
}
}

View File

@@ -1,11 +1,10 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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,53 +51,53 @@ 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;
currentSheet = s.Sheet;
}
public void DrawSprite(Sprite s, float3 location, PaletteReference pal)
public void DrawSprite(Sprite s, float2 location, PaletteReference pal)
{
DrawSprite(s, location, pal.TextureIndex, s.Size);
}
public void DrawSprite(Sprite s, float3 location, PaletteReference pal, float3 size)
public void DrawSprite(Sprite s, float2 location, PaletteReference pal, float2 size)
{
DrawSprite(s, location, pal.TextureIndex, size);
}
void DrawSprite(Sprite s, float3 location, float paletteTextureIndex, float3 size)
void DrawSprite(Sprite s, float2 location, float paletteTextureIndex, float2 size)
{
SetRenderStateForSprite(s);
Util.FastCreateQuad(vertices, location + s.FractionalOffset * size, s, paletteTextureIndex, nv, size);
nv += 6;
nv += 4;
}
// For RGBASpriteRenderer, which doesn't use palettes
public void DrawSprite(Sprite s, float3 location)
public void DrawSprite(Sprite s, float2 location)
{
DrawSprite(s, location, 0, s.Size);
}
public void DrawSprite(Sprite s, float3 location, float3 size)
public void DrawSprite(Sprite s, float2 location, float2 size)
{
DrawSprite(s, location, 0, size);
}
public void DrawSprite(Sprite s, float3 a, float3 b, float3 c, float3 d)
public void DrawSprite(Sprite s, float2 a, float2 b, float2 c, float2 d)
{
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)
@@ -114,17 +113,11 @@ namespace OpenRA.Graphics
shader.SetTexture("Palette", palette);
}
public void SetViewportParams(Size screen, float depthScale, float depthOffset, float zoom, int2 scroll)
public void SetViewportParams(Size screen, float zoom, int2 scroll)
{
shader.SetVec("Scroll", scroll.X, scroll.Y, scroll.Y);
shader.SetVec("r1",
zoom * 2f / screen.Width,
-zoom * 2f / screen.Height,
-depthScale * zoom / screen.Height);
shader.SetVec("r2", -1, 1, 1 - depthOffset);
// Texture index is sampled as a float, so convert to pixels then scale
shader.SetVec("DepthTextureScale", 128 * depthScale * zoom / screen.Height);
shader.SetVec("Scroll", scroll.X, scroll.Y);
shader.SetVec("r1", zoom * 2f / screen.Width, -zoom * 2f / screen.Height);
shader.SetVec("r2", -1, 1);
}
public void SetDepthPreviewEnabled(bool enabled)

View File

@@ -1,17 +1,18 @@
#region Copyright & License Information
/*
* Copyright 2007-2017 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.Screen3DPosition(waypoints.First());
var first = wr.ScreenPxPosition(waypoints.First());
var a = first;
foreach (var b in waypoints.Skip(1).Select(pos => wr.Screen3DPosition(pos)))
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, float3 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) { }

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