Skip to content

Commit c052271

Browse files
committed
First release
0 parents  commit c052271

17 files changed

+1008
-0
lines changed

.gitignore

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
*.gem
2+
*.rbc
3+
.bundle
4+
.config
5+
.yardoc
6+
Gemfile.lock
7+
InstalledFiles
8+
_yardoc
9+
coverage
10+
doc/
11+
lib/bundler/man
12+
pkg
13+
rdoc
14+
spec/reports
15+
test/tmp
16+
test/version_tmp
17+
tmp

.yardopts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--markup markdown

Gemfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
source 'https://rubygems.org'
2+
3+
group :development, :test do
4+
gem 'redcarpet', :platforms => [:ruby, :mswin, :mingw]
5+
gem 'coveralls', require: false if ENV['CI']
6+
end
7+
8+
# Specify your gem's dependencies in SteamCodec.gemspec
9+
gemspec

README.md

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# SteamCodec
2+
3+
SteamCodec is a library for working with different [Steam client](http://store.steampowered.com/about/) (and [Source engine](http://source.valvesoftware.com/)) file formats.
4+
5+
Currently supported formats:
6+
7+
* [KeyValues](https://developer.valvesoftware.com/wiki/KeyValues)
8+
* VDF (Valve Data Format)
9+
* ACF (ApplicationCacheFile)
10+
11+
12+
PKV (packed KeyValues) isn't supported yet.
13+
14+
15+
## Installation
16+
17+
Add this line to your application's Gemfile:
18+
19+
```ruby
20+
gem 'steam_codec'
21+
```
22+
23+
And then execute:
24+
25+
```shell
26+
bundle
27+
```
28+
29+
Or install it yourself as:
30+
31+
```shell
32+
gem install SteamCodec
33+
```
34+
35+
### Dependencies
36+
37+
gem `insensitive_hash`
38+
39+
## Usage
40+
41+
```ruby
42+
require 'steam_codec'
43+
44+
File.open("appmanifest_220.acf") do |file|
45+
acf = SteamCodec::ACF::loadFromFile(file)
46+
puts acf.UserConfig.Name
47+
end
48+
```
49+
50+
## Documentation
51+
52+
YARD with markdown is used for documentation (`redcarpet` required)
53+
54+
## Specs
55+
56+
RSpec and simplecov are required, to run tests just `rake spec`
57+
code coverage will also be generated
58+
59+
## Code status
60+
61+
[![Build Status](https://travis-ci.org/davispuh/SteamCodec.png?branch=master)](https://travis-ci.org/davispuh/SteamCodec)
62+
[![Dependency Status](https://gemnasium.com/davispuh/SteamCodec.png)](https://gemnasium.com/davispuh/SteamCodec)
63+
[![Coverage Status](https://coveralls.io/repos/davispuh/SteamCodec/badge.png)](https://coveralls.io/r/davispuh/SteamCodec)
64+
65+
## Unlicense
66+
67+
![Copyright-Free](http://unlicense.org/pd-icon.png)
68+
69+
All text, documentation, code and files in this repository are in public domain (including this text, README).
70+
It means you can copy, modify, distribute and include in your own work/code, even for commercial purposes, all without asking permission.
71+
72+
[About Unlicense](http://unlicense.org/)
73+
74+
## Contributing
75+
76+
Feel free to improve anything.
77+
78+
1. Fork it
79+
2. Create your feature branch (`git checkout -b my-new-feature`)
80+
3. Commit your changes (`git commit -am 'Add some feature'`)
81+
4. Push to the branch (`git push origin my-new-feature`)
82+
5. Create new Pull Request
83+
84+
85+
**Warning**: By sending pull request to this repository you dedicate any and all copyright interest in pull request (code files and all other) to the public domain. (files will be in public domain even if pull request doesn't get merged)
86+
87+
Also before sending pull request you acknowledge that you own all copyrights or have authorization to dedicate them to public domain.
88+
89+
If you don't want to dedicate code to public domain or if you're not allowed to (eg. you don't own required copyrights) then DON'T send pull request.
90+

Rakefile

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
require "bundler/gem_tasks"
2+
require 'rspec/core/rake_task'
3+
require 'yard'
4+
5+
desc 'Default: run specs.'
6+
task :default => :spec
7+
8+
desc 'Run specs'
9+
RSpec::Core::RakeTask.new(:spec) do |t|
10+
end
11+
12+
YARD::Rake::YardocTask.new(:doc) do |t|
13+
end

UNLICENSE

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
This is free and unencumbered software released into the public domain.
2+
3+
Anyone is free to copy, modify, publish, use, compile, sell, or
4+
distribute this software, either in source code form or as a compiled
5+
binary, for any purpose, commercial or non-commercial, and by any
6+
means.
7+
8+
In jurisdictions that recognize copyright laws, the author or authors
9+
of this software dedicate any and all copyright interest in the
10+
software to the public domain. We make this dedication for the benefit
11+
of the public at large and to the detriment of our heirs and
12+
successors. We intend this dedication to be an overt act of
13+
relinquishment in perpetuity of all present and future rights to this
14+
software under copyright law.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
OTHER DEALINGS IN THE SOFTWARE.
23+
24+
For more information, please refer to <http://unlicense.org/>

lib/steam_codec.rb

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
require "steam_codec/version"
2+
require "steam_codec/key_values"
3+
require "steam_codec/value_array"
4+
require "steam_codec/vdf"
5+
require "steam_codec/acf"

lib/steam_codec/ACF.rb

+176
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
module SteamCodec
2+
class ACF
3+
4+
# More about AppID => https://developer.valvesoftware.com/wiki/Steam_Application_IDs
5+
attr_accessor :AppID
6+
attr_accessor :Universe
7+
attr_accessor :StateFlags
8+
attr_accessor :InstallDir
9+
attr_accessor :LastUpdated
10+
attr_accessor :UpdateResult
11+
attr_accessor :SizeOnDisk
12+
attr_accessor :BuildID
13+
attr_accessor :LastOwner
14+
attr_accessor :BytesToDownload
15+
attr_accessor :BytesDownloaded
16+
attr_accessor :FullValidateOnNextUpdate
17+
attr_reader :UserConfig
18+
attr_reader :MountedDepots
19+
attr_reader :SharedDepots
20+
attr_reader :CheckGuid
21+
attr_reader :InstallScripts
22+
def self.loadFromFile(file)
23+
acf = KeyValues::loadFromFile(file)
24+
return self.new(acf.AppState) if acf and acf.key?(:AppState)
25+
nil
26+
end
27+
28+
def self.load(data)
29+
acf = KeyValues::load(data)
30+
return self.new(acf.AppState) if acf and acf.key?(:AppState)
31+
nil
32+
end
33+
34+
def initialize(appState = nil)
35+
load(appState || KeyValues.new)
36+
end
37+
38+
def load(appState)
39+
raise ArgumentError, "AppState must be instance of KeyValues" unless appState.is_a?(KeyValues)
40+
@AppState = appState
41+
@AppID = @AppState.AppID.to_i if @AppState.key?(:AppID)
42+
@Universe = @AppState.Universe.to_i if @AppState.key?(:Universe)
43+
@StateFlags = @AppState.StateFlags.to_i if @AppState.key?(:StateFlags)
44+
@InstallDir = @AppState.InstallDir if @AppState.key?(:InstallDir)
45+
@LastUpdated = @AppState.LastUpdated.to_i if @AppState.key?(:LastUpdated)
46+
@UpdateResult = @AppState.UpdateResult.to_i if @AppState.key?(:UpdateResult)
47+
@SizeOnDisk = @AppState.SizeOnDisk.to_i if @AppState.key?(:SizeOnDisk)
48+
@BuildID = @AppState.BuildID.to_i if @AppState.key?(:BuildID)
49+
@LastOwner = @AppState.LastOwner if @AppState.key?(:LastOwner)
50+
@BytesToDownload = @AppState.BytesToDownload.to_i if @AppState.key?(:BytesToDownload)
51+
@BytesDownloaded = @AppState.BytesDownloaded.to_i if @AppState.key?(:BytesDownloaded)
52+
@FullValidateOnNextUpdate = !@AppState.FullValidateOnNextUpdate.to_i.zero? if @AppState.key?(:FullValidateOnNextUpdate)
53+
userConfig = nil
54+
mountedDepots = {}
55+
sharedDepots = {}
56+
checkGuid = {}
57+
installScripts = {}
58+
userConfig = @AppState.UserConfig if @AppState.key?(:UserConfig)
59+
mountedDepots = @AppState.MountedDepots if @AppState.key?(:MountedDepots)
60+
sharedDepots = @AppState.SharedDepots if @AppState.key?(:sharedDepots)
61+
checkGuid = @AppState.CheckGuid if @AppState.key?(:CheckGuid)
62+
installScripts = @AppState.InstallScripts if @AppState.key?(:InstallScripts)
63+
@UserConfig = UserConfig.new(userConfig)
64+
@MountedDepots = MountedDepots.new(mountedDepots)
65+
@SharedDepots = SharedDepots.new(sharedDepots)
66+
@InstallScripts = InstallScripts.new(installScripts)
67+
end
68+
69+
class UserConfig
70+
attr_accessor :Name
71+
attr_accessor :GameID
72+
attr_accessor :Installed
73+
attr_accessor :AppInstallDir
74+
attr_accessor :Language
75+
attr_accessor :BetaKey
76+
def initialize(userConfig = nil)
77+
load(userConfig || KeyValues.new)
78+
end
79+
80+
def load(userConfig)
81+
raise ArgumentError, "UserConfig must be instance of KeyValues" unless userConfig.is_a?(KeyValues)
82+
@UserConfig = userConfig
83+
@Name = @UserConfig.name if @UserConfig.key?(:name)
84+
@GameID = @UserConfig.gameid.to_i if @UserConfig.key?(:gameid)
85+
@Installed = !@UserConfig.installdir.to_i.zero? if @UserConfig.key?(:installdir)
86+
@AppInstallDir = @UserConfig.appinstalldir if @UserConfig.key?(:appinstalldir)
87+
@Language = @UserConfig.language if @UserConfig.key?(:language)
88+
@BetaKey = @UserConfig.BetaKey if @UserConfig.key?(:BetaKey)
89+
end
90+
end
91+
92+
class MountedDepots
93+
def initialize(mountedDepots = {})
94+
load(mountedDepots)
95+
end
96+
97+
def load(mountedDepots)
98+
raise ArgumentError, "MountedDepots must be instance of Hash" unless mountedDepots.is_a?(Hash)
99+
@MountedDepots = {}
100+
mountedDepots.each do |depot, manifest|
101+
@MountedDepots[depot.to_i] = manifest
102+
end
103+
end
104+
105+
def depots
106+
@MountedDepots.keys
107+
end
108+
109+
def manifests
110+
@MountedDepots.values
111+
end
112+
113+
def getManifest(depotID)
114+
@MountedDepots.each do |depot, manifest|
115+
return manifest if depot == depotID
116+
end
117+
nil
118+
end
119+
120+
def getDepot(manifestID)
121+
@MountedDepots.each do |depot, manifest|
122+
return depot if manifest == manifestID
123+
end
124+
nil
125+
end
126+
127+
def set(depot, manifest)
128+
@MountedDepots[depot] = manifest
129+
end
130+
131+
def remove(depot)
132+
@MountedDepots.delete(depot)
133+
end
134+
end
135+
136+
class SharedDepots
137+
attr_reader :Depots
138+
def initialize(sharedDepots = {})
139+
load(sharedDepots)
140+
end
141+
142+
def load(sharedDepots)
143+
raise ArgumentError, "SharedDepots must be instance of Hash" unless sharedDepots.is_a?(Hash)
144+
@SharedDepots = {}
145+
sharedDepots.each do |depot, baseDepot|
146+
@SharedDepots[depot.to_i] = baseDepot.to_i
147+
end
148+
end
149+
150+
def depots
151+
@SharedDepots.keys
152+
end
153+
154+
def getDepot(depotID)
155+
@SharedDepots.each do |depot, baseDepot|
156+
return baseDepot if depot == depotID
157+
end
158+
nil
159+
end
160+
161+
def set(depot, baseDepot)
162+
@SharedDepots[depot] = baseDepot
163+
end
164+
165+
def remove(depot)
166+
@SharedDepots.delete(depot)
167+
end
168+
end
169+
170+
class CheckGuid < ValueArray
171+
end
172+
173+
class InstallScripts < ValueArray
174+
end
175+
end
176+
end

0 commit comments

Comments
 (0)