dsp_blueprint_parser 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,89 +1,108 @@
1
- # frozen_string_literal: true
2
-
3
- module DspBlueprintParser
4
- # class to orchestrate parsing
5
- class Parser
6
- SECONDS_AT_EPOC = 62_135_596_800
7
-
8
- # @param [String] str_blueprint
9
- def initialize(str_blueprint)
10
- @data_sections = DataSections.new(str_blueprint)
11
- end
12
-
13
- # @return [BlueprintData]
14
- def blueprint
15
- @blueprint ||= BlueprintData.new.tap do |blueprint|
16
- parse_metadata(blueprint)
17
- parse_areas(blueprint)
18
- parse_buildings(blueprint)
19
- end
20
- end
21
-
22
- private
23
-
24
- # @return [Array<String>]
25
- def header_segments
26
- @header_segments ||= @data_sections.header_segments
27
- end
28
-
29
- # @return [BinaryReader]
30
- def reader
31
- @reader ||= BinaryReader.new(@data_sections.decompressed_body)
32
- end
33
-
34
- # @param ticks [Integer]
35
- # @return [Time]
36
- def ticks_to_epoch(ticks)
37
- # 10mil ticks per second
38
- seconds = ticks / 10_000_000
39
-
40
- Time.at(seconds - SECONDS_AT_EPOC)
41
- end
42
-
43
- # @param [BlueprintData] blueprint
44
- def parse_areas(blueprint)
45
- reader.read_i8.times do
46
- area = Area.new
47
- area.index = reader.read_i8
48
- area.parent_index = reader.read_i8
49
- area.tropic_anchor = reader.read_i16
50
- area.area_segments = reader.read_i16
51
- area.anchor_local_offset_x = reader.read_i16
52
- area.anchor_local_offset_y = reader.read_i16
53
- area.width = reader.read_i16
54
- area.height = reader.read_i16
55
-
56
- blueprint.areas << area
57
- end
58
- end
59
-
60
- # @param [BlueprintData] blueprint
61
- def parse_buildings(blueprint)
62
- BuildingParser.process!(blueprint, reader)
63
- end
64
-
65
- # @param [BlueprintData] blueprint
66
- def parse_metadata(blueprint)
67
- blueprint.icon_layout = header_segments[1].to_i
68
- blueprint.icon0 = header_segments[2].to_i
69
- blueprint.icon1 = header_segments[3].to_i
70
- blueprint.icon2 = header_segments[4].to_i
71
- blueprint.icon3 = header_segments[5].to_i
72
- blueprint.icon4 = header_segments[6].to_i
73
-
74
- blueprint.time = ticks_to_epoch(header_segments[8].to_i)
75
- blueprint.game_version = header_segments[9]
76
-
77
- blueprint.short_description = CGI.unescape(header_segments[10]) if header_segments[10]
78
- blueprint.description = CGI.unescape(header_segments[11]) if header_segments[11]
79
-
80
- blueprint.version = reader.read_i32
81
- blueprint.cursor_offset_x = reader.read_i32
82
- blueprint.cursor_offset_y = reader.read_i32
83
- blueprint.cursor_target_area = reader.read_i32
84
- blueprint.drag_box_size_x = reader.read_i32
85
- blueprint.drag_box_size_y = reader.read_i32
86
- blueprint.primary_area_idx = reader.read_i32
87
- end
88
- end
89
- end
1
+ # frozen_string_literal: true
2
+
3
+ module DspBlueprintParser
4
+ # class to orchestrate parsing
5
+ class Parser
6
+ SECONDS_AT_EPOC = 62_135_596_800
7
+ URI_PARSER = URI::Parser.new.freeze
8
+
9
+ # @param [String] str_blueprint
10
+ def initialize(str_blueprint)
11
+ @data_sections = DataSections.new(str_blueprint)
12
+ end
13
+
14
+ # @return [BlueprintData]
15
+ def blueprint
16
+ @blueprint ||= BlueprintData.new.tap do |blueprint|
17
+ parse_metadata(blueprint)
18
+ parse_areas(blueprint)
19
+ parse_buildings(blueprint)
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ # @return [Array<String>]
26
+ def header_segments
27
+ @header_segments ||= @data_sections.header_segments
28
+ end
29
+
30
+ # @return [BinaryReader]
31
+ def reader
32
+ @reader ||= BinaryReader.new(@data_sections.decompressed_body)
33
+ end
34
+
35
+ # @param ticks [Integer]
36
+ # @return [Time]
37
+ def ticks_to_epoch(ticks)
38
+ # 10mil ticks per second
39
+ seconds = ticks / 10_000_000
40
+
41
+ Time.at(seconds - SECONDS_AT_EPOC)
42
+ end
43
+
44
+ # @param [BlueprintData] blueprint
45
+ def parse_areas(blueprint)
46
+ reader.read_i8.times do
47
+ area = Area.new
48
+ area.index = reader.read_i8
49
+ area.parent_index = reader.read_i8
50
+ area.tropic_anchor = reader.read_i16
51
+ area.area_segments = reader.read_i16
52
+ area.anchor_local_offset_x = reader.read_i16
53
+ area.anchor_local_offset_y = reader.read_i16
54
+ area.width = reader.read_i16
55
+ area.height = reader.read_i16
56
+
57
+ blueprint.areas << area
58
+ end
59
+ end
60
+
61
+ # @param [BlueprintData] blueprint
62
+ def parse_buildings(blueprint)
63
+ BuildingParser.process!(blueprint, reader)
64
+ end
65
+
66
+ # @param [BlueprintData] blueprint
67
+ def parse_metadata(blueprint)
68
+ header_version = header_segments[0].gsub(':', '').to_i
69
+
70
+ blueprint.icon_layout = header_segments[1].to_i
71
+ blueprint.icon0 = header_segments[2].to_i
72
+ blueprint.icon1 = header_segments[3].to_i
73
+ blueprint.icon2 = header_segments[4].to_i
74
+ blueprint.icon3 = header_segments[5].to_i
75
+ blueprint.icon4 = header_segments[6].to_i
76
+
77
+ blueprint.time = ticks_to_epoch(header_segments[8].to_i)
78
+ blueprint.game_version = header_segments[9]
79
+
80
+ blueprint.short_description = uri_unescape(header_segments[10])
81
+
82
+ if header_version >= 1
83
+ blueprint.author = uri_unescape(header_segments[11])
84
+ blueprint.custom_version = uri_unescape(header_segments[12])
85
+ blueprint.attributes = uri_unescape(header_segments[13])&.split(';')
86
+ blueprint.description = uri_unescape(header_segments[14])
87
+ elsif header_segments[11]
88
+ blueprint.description = uri_unescape(header_segments[11])
89
+ end
90
+
91
+ blueprint.version = reader.read_i32
92
+ blueprint.cursor_offset_x = reader.read_i32
93
+ blueprint.cursor_offset_y = reader.read_i32
94
+ blueprint.cursor_target_area = reader.read_i32
95
+ blueprint.drag_box_size_x = reader.read_i32
96
+ blueprint.drag_box_size_y = reader.read_i32
97
+ blueprint.primary_area_idx = reader.read_i32
98
+ end
99
+
100
+ # @param input [String, nil]
101
+ # @return [String, nil]
102
+ def uri_unescape(input)
103
+ return nil if input.nil?
104
+
105
+ URI_PARSER.unescape(input)
106
+ end
107
+ end
108
+ end
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- module DspBlueprintParser
4
- VERSION = '0.2.2'
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ module DspBlueprintParser
4
+ VERSION = '0.2.3'
5
+ end
@@ -1,46 +1,46 @@
1
- # frozen_string_literal: true
2
-
3
- require 'date'
4
- require 'zlib'
5
- require 'stringio'
6
- require 'base64'
7
- require 'md5f'
8
-
9
- require_relative 'dsp_blueprint_parser/version'
10
- require_relative 'dsp_blueprint_parser/blueprint_data'
11
- require_relative 'dsp_blueprint_parser/icon_layout'
12
- require_relative 'dsp_blueprint_parser/area'
13
- require_relative 'dsp_blueprint_parser/building'
14
- require_relative 'dsp_blueprint_parser/binary_reader'
15
- require_relative 'dsp_blueprint_parser/parser'
16
- require_relative 'dsp_blueprint_parser/building_parser'
17
- require_relative 'dsp_blueprint_parser/data_sections'
18
-
19
- BLUEPRINT_TYPE = /(BLUEPRINT|DYBP):/
20
-
21
- # module to receive a Dyson Sphere Program blueprint string and parse it
22
- module DspBlueprintParser
23
- class Error < StandardError; end
24
-
25
- # @param str_blueprint [String]
26
- # @return [BlueprintData]
27
- def self.parse(str_blueprint)
28
- return if str_blueprint.size < 28
29
- return unless str_blueprint.start_with? BLUEPRINT_TYPE
30
-
31
- parser = Parser.new(str_blueprint)
32
- parser.blueprint
33
- end
34
-
35
- # @param input [String]
36
- # @return [Boolean]
37
- def self.is_valid?(input)
38
- return false if input.size < 28
39
- return false unless input.start_with? BLUEPRINT_TYPE
40
-
41
- sections = DataSections.new(input)
42
- hash = MD5F::compute(sections.hashed_string)
43
-
44
- return sections.hash == hash
45
- end
46
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'date'
4
+ require 'zlib'
5
+ require 'stringio'
6
+ require 'base64'
7
+ require 'md5f'
8
+
9
+ require_relative 'dsp_blueprint_parser/version'
10
+ require_relative 'dsp_blueprint_parser/blueprint_data'
11
+ require_relative 'dsp_blueprint_parser/icon_layout'
12
+ require_relative 'dsp_blueprint_parser/area'
13
+ require_relative 'dsp_blueprint_parser/building'
14
+ require_relative 'dsp_blueprint_parser/binary_reader'
15
+ require_relative 'dsp_blueprint_parser/parser'
16
+ require_relative 'dsp_blueprint_parser/building_parser'
17
+ require_relative 'dsp_blueprint_parser/data_sections'
18
+
19
+ BLUEPRINT_TYPE = /(BLUEPRINT|DYBP):/
20
+
21
+ # module to receive a Dyson Sphere Program blueprint string and parse it
22
+ module DspBlueprintParser
23
+ class Error < StandardError; end
24
+
25
+ # @param str_blueprint [String]
26
+ # @return [BlueprintData]
27
+ def self.parse(str_blueprint)
28
+ return if str_blueprint.size < 28
29
+ return unless str_blueprint.start_with? BLUEPRINT_TYPE
30
+
31
+ parser = Parser.new(str_blueprint)
32
+ parser.blueprint
33
+ end
34
+
35
+ # @param input [String]
36
+ # @return [Boolean]
37
+ def self.is_valid?(input)
38
+ return false if input.size < 28
39
+ return false unless input.start_with? BLUEPRINT_TYPE
40
+
41
+ sections = DataSections.new(input)
42
+ hash = MD5F::compute(sections.hashed_string)
43
+
44
+ return sections.hash == hash
45
+ end
46
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dsp_blueprint_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Falk
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-12-28 00:00:00.000000000 Z
11
+ date: 2026-01-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake-compiler