prison_parser 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 75a9c80e97dedb4608da793cacfdc8fa6905d4f2
4
+ data.tar.gz: 9346ac58194348db220677e267918375cb78be48
5
+ SHA512:
6
+ metadata.gz: a0438293e09a82e458a9a2ae476e2858dff53a449d69caf728f9c455db1845d3f0cc4b0ffe440b7cfb0d9414df7c658d275bccbf583550ec6280b0113d98c6de
7
+ data.tar.gz: ab4562d8aa3f1439c2bf6f67eec72d14a28ee6f5e79334dd1d19a42f2df39dc12516f9cd39cf955aafa5309d5ee1ed38fb614c6bec5c8831b8446152e3ae88a2
data/.gitignore ADDED
@@ -0,0 +1,21 @@
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
18
+
19
+ /docs/
20
+
21
+ /.rspec
data/.yardopts ADDED
@@ -0,0 +1,6 @@
1
+ --no-cache
2
+ --no-private
3
+ --hide-void-return
4
+ --output-dir ./docs
5
+ --readme README.md
6
+ lib/**/*.rb
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Mitch Dempsey
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
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
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,39 @@
1
+ # Prison Architect Parser
2
+
3
+ This gem allows you to parse, access, and manipulate a save file from Prison Architect.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'prison_parser'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install prison_parser
18
+
19
+ ## Usage
20
+
21
+ ```ruby
22
+ require 'prison_parser'
23
+ prison = PrisonParser::Prison.open("myprison.prison")
24
+
25
+ puts prison.Finance.Balance # 1318560.0
26
+
27
+ prison.save("newprison.prison")
28
+
29
+ ```
30
+
31
+ ## Roadmap
32
+
33
+ * Ability to convert a prison to a blank, but planned prison.
34
+
35
+ ## Thanks
36
+ Special thanks to: [Matvei Stefarov](https://github.com/fragmer) for his work on [PASaveEditor](https://github.com/fragmer/PASaveEditor), which served as the basis for the parsing implementation. To [Jason Dew](https://github.com/jasondew) for letting me use the name of his original library, [prison_parser](https://github.com/jasondew/prison_parser)
37
+
38
+ ## License
39
+ Please see the [LICENSE](https://github.com/webdestroya/prison_parser/LICENSE) for more information.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,9 @@
1
+ module PrisonParser
2
+
3
+ end
4
+
5
+ require_relative "prison_parser/version"
6
+ require_relative "prison_parser/node"
7
+ require_relative "prison_parser/models"
8
+ require_relative "prison_parser/prison"
9
+ require_relative "prison_parser/utils"
@@ -0,0 +1,7 @@
1
+ module PrisonParser
2
+ module Models
3
+
4
+ end
5
+ end
6
+ require_relative "models/base"
7
+ require_relative "models/finance"
@@ -0,0 +1,11 @@
1
+ module PrisonParser
2
+ module Models
3
+ class Base < Node
4
+ def initialize
5
+ class_name = self.class.name
6
+ label = class_name[(class_name.rindex("::") + 2)..-1]
7
+ super(label)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module PrisonParser
2
+ module Models
3
+ class Finance < Base
4
+
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,99 @@
1
+ module PrisonParser
2
+ class Node
3
+ attr_reader :nodes
4
+ attr_reader :properties
5
+ attr_reader :label
6
+
7
+ attr_accessor :allow_inline
8
+
9
+ def initialize(label=nil)
10
+ @label = label
11
+ @nodes = Hash.new
12
+ @properties = Hash.new
13
+ @allow_inline = true
14
+ end
15
+
16
+ def method_missing(name, *args, &block)
17
+ # Look in Nodes
18
+ # Look in properties
19
+ # if ends with ?, cast property to boolean
20
+ # else SUPER
21
+
22
+ if nodes.has_key?(name.to_s)
23
+ nodes[name.to_s]
24
+ elsif properties.has_key?(name.to_s)
25
+ properties[name.to_s]
26
+ else
27
+ super
28
+ end
29
+ end
30
+
31
+ def allow_inline?
32
+ @allow_inline
33
+ end
34
+
35
+ def inspect
36
+ str = StringIO.new("")
37
+ str.write "<#{label} "
38
+ str.write properties.entries.map { |e|"#{e[0]}=\"#{e[1]}\"" }.join(", ")
39
+ str.write ">"
40
+ str.string
41
+ end
42
+
43
+ # ReadKey, PushProperty
44
+ def add_property(key, value)
45
+ return if key == "BEGIN" || key == "END"
46
+ if properties[key]
47
+ if properties[key].is_a?(Array)
48
+ @properties[key] << value
49
+ else
50
+ @properties[key] = [properties[key], value]
51
+ end
52
+ else
53
+ @properties[key] = value
54
+ end
55
+ end
56
+
57
+ def create_node(node_label)
58
+ node = Node.new(node_label)
59
+ if nodes.has_key?(node_label)
60
+ if nodes[node_label].is_a?(Array)
61
+ @nodes[node_label] << node
62
+ else
63
+ @nodes[node_label] = [nodes[node_label], node]
64
+ end
65
+ else
66
+ @nodes[node_label] = node
67
+ end
68
+ node
69
+ end
70
+
71
+ def finished_reading_node(node)
72
+ # do nothing
73
+ end
74
+
75
+
76
+ # WRITING
77
+
78
+ def write_nodes(writer)
79
+ nodes.values.each do |node|
80
+ if node.is_a?(Array)
81
+ node.map { |n| writer.write_node(n) }
82
+ else
83
+ writer.write_node(node)
84
+ end
85
+ end
86
+ end
87
+
88
+ def write_properties(writer)
89
+ properties.each_pair do |k,value|
90
+ if value.is_a?(Array)
91
+ value.map { |v| writer.write_property(k, v) }
92
+ else
93
+ writer.write_property(k, value)
94
+ end
95
+ end
96
+ end
97
+
98
+ end
99
+ end
@@ -0,0 +1,30 @@
1
+ module PrisonParser
2
+ class Prison < Node
3
+
4
+ def initialize
5
+ super("Prison")
6
+ end
7
+
8
+ # Open a prison from the file specified
9
+ def self.open(filename)
10
+ PrisonParser::Utils::Parser.new.load(filename)
11
+ end
12
+
13
+ # Save the prison to the specified filename
14
+ def save(filename)
15
+ file = File.open(filename, "w+")
16
+ PrisonParser::Utils::Writer.new(file).write_prison(self)
17
+ end
18
+
19
+ def create_node(node_label)
20
+ if PrisonParser::Models.const_defined?(node_label)
21
+ node = PrisonParser::Models.const_get(node_label).new
22
+ @nodes[node.label] = node
23
+ node
24
+ else
25
+ super
26
+ end
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ module PrisonParser
2
+ module Utils
3
+
4
+ end
5
+ end
6
+
7
+ require_relative "utils/parser"
8
+ require_relative "utils/writer"
@@ -0,0 +1,118 @@
1
+ module PrisonParser
2
+ module Utils
3
+ class Parser
4
+
5
+ attr_reader :tokens
6
+
7
+ def initialize
8
+ @tokens = []
9
+ end
10
+
11
+ def load(filepath)
12
+ line_num = 0
13
+ nodes = []
14
+ @tokens = []
15
+ currentNode = Prison.new
16
+
17
+ file = File.open(filepath, "r")
18
+
19
+ while !file.eof? do
20
+ line = file.readline.strip
21
+ line_num += 1
22
+ tokenize(line)
23
+
24
+ next if tokens.size == 0
25
+
26
+ # start a new node
27
+ if "BEGIN" == tokens[0]
28
+ nodes.push(currentNode)
29
+
30
+ label = tokens[1]
31
+ currentNode = currentNode.create_node(label)
32
+
33
+ if tokens.size > 2
34
+ # inline node
35
+ if tokens.size % 2 != 1
36
+ raise "Unexpected number of tokens in an inline node definition on line #{line_num}"
37
+ end
38
+
39
+ unless "END" == tokens[tokens.size - 1]
40
+ raise "Unexpected end of inline node definition on line #{line_num}"
41
+ end
42
+
43
+ tokens.each_slice(2) do |parts|
44
+ # the first one is bogus :/
45
+ currentNode.add_property(parts[0], parts[1])
46
+ end
47
+
48
+ upperNode = nodes.pop
49
+ upperNode.finished_reading_node(currentNode)
50
+ currentNode = upperNode
51
+ else
52
+ currentNode.allow_inline = false
53
+ end
54
+ elsif "END" == tokens[0]
55
+ # end of multi-line section
56
+ upperNode = nodes.pop
57
+ upperNode.finished_reading_node(currentNode)
58
+ currentNode = upperNode
59
+ else
60
+ # inside a multi-line section
61
+ currentNode.add_property(tokens[0], tokens[1])
62
+ end
63
+ end
64
+
65
+ if nodes.size != 0
66
+ raise "Unexpected end of file!"
67
+ end
68
+
69
+ currentNode
70
+ end
71
+
72
+ # Splits a line into a series of tokens
73
+ def tokenize(line)
74
+ return if line.size == 0
75
+
76
+ @tokens.clear
77
+
78
+ token_start = 0
79
+ i = 0
80
+ chars = line.chars
81
+
82
+ while i < line.size do
83
+ c = chars[i]
84
+ if c == ' '
85
+ # eat the spaces!
86
+ if token_start != i
87
+ @tokens << line[token_start, i - token_start]
88
+ end
89
+ token_start = i + 1
90
+ elsif c == '"'
91
+ # skip ahead to the next quote
92
+ end_quotes = line.index('"', i + 1)
93
+
94
+ end_quotes = -1 if end_quotes.nil?
95
+
96
+ @tokens << line[i + 1, end_quotes - i - 1]
97
+ i = end_quotes
98
+ token_start = i + 1
99
+ else
100
+ # skip ahead to the next space
101
+ next_space = line.index(' ', i)
102
+ next_space = -1 if next_space.nil?
103
+ i = next_space - 1
104
+ break if (i < 0)
105
+ end
106
+ i += 1
107
+ end
108
+
109
+ if token_start < line.size
110
+ # append the remainder of the string, after we ran out of spaces
111
+ @tokens << line[token_start, line.size - token_start]
112
+ end
113
+
114
+ return @tokens
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,96 @@
1
+ module PrisonParser
2
+ module Utils
3
+ class Writer
4
+ INDENT_STR = " "
5
+
6
+ attr_reader :stream
7
+
8
+
9
+ def initialize(stream=nil)
10
+ @stream = stream || StringIO.new("")
11
+ @inline = false
12
+ @inline_stack = []
13
+ @indent = 0
14
+ end
15
+
16
+ def write_prison(prison)
17
+ write "\n"
18
+ write_node_data(prison)
19
+ self
20
+ end
21
+
22
+ def write_node(node)
23
+ return nil if node.nil?
24
+ @inline_stack.push(@inline)
25
+ @inline = node_inline?(node)
26
+
27
+ write_indent
28
+ @indent += 1
29
+ write "BEGIN "
30
+ write_value node.label
31
+
32
+ if @inline
33
+ write " "
34
+ else
35
+ write "\n"
36
+ end
37
+
38
+ write_node_data(node)
39
+
40
+ @indent -= 1
41
+
42
+ write_indent unless @inline
43
+ write "END\n"
44
+
45
+ @inline = @inline_stack.pop
46
+
47
+ self
48
+ end
49
+
50
+ def write_indent(indent = nil)
51
+ indent ||= @indent
52
+ return if indent <= 0
53
+ write INDENT_STR*indent
54
+ end
55
+
56
+ def write_property(key, value)
57
+ return if value.nil?
58
+
59
+ write_indent unless @inline
60
+
61
+ write_value key
62
+ write " "
63
+ write_value value
64
+
65
+ write (@inline ? " " : "\n")
66
+
67
+ end
68
+
69
+ def write_node_data(node)
70
+ node.write_properties(self)
71
+ node.write_nodes(self)
72
+ end
73
+
74
+ def write_value(value)
75
+ if quote?(value)
76
+ write "\"#{value}\""
77
+ else
78
+ write value
79
+ end
80
+ end
81
+
82
+ def node_inline?(node)
83
+ node.nodes.size == 0 && node.properties.size < 13 && node.allow_inline?
84
+ end
85
+
86
+ def quote?(value)
87
+ !value.index(' ').nil?
88
+ end
89
+
90
+ def write(data)
91
+ stream.write data
92
+ end
93
+
94
+ end
95
+ end
96
+ end