summaryrefslogtreecommitdiffhomepage
path: root/game/study_player/config.rb
blob: 87ffb7a97d4ddbe8b245baf1b4acd1e75011d4c5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# Study Player — config persistence (Phase 6).
#
# Loads/saves a key=value INI file (study-player.cfg) in the current working
# directory. Format matches the original ../source/src/config.c INI style so
# existing config files are forward-compatible.
#
# Persisted settings (meaningful for the RmlUi UI):
#   window_width, window_height  — Rl.init_window size
#   study_mode_default           — whether study mode starts ON
#   volume                       — 0.0 .. 1.0
#   last_audio_path              — last file loaded (for quick reload)
#   font_scale                   — applied to the body element
#
# Plus all original UILayout keys mapped to RmlUi element properties
# (see Layout module for the mapping).
#
# See: notes/study-player-rewrite-plan.md §9 (layout persistence)
#      ../source/src/config.c (original INI loader)

module StudyPlayer
  module Config
    CFG_FILE = "study-player.cfg".freeze

    # Default values. Keys are symbols (Ruby); saved as strings (INI).
    DEFAULTS = {
      window_width:        1280,
      window_height:       720,
      study_mode_default:  false,
      volume:              1.0,
      last_audio_path:     "",
      font_scale:          1.0,
      # Original UILayout keys (will be mapped to RmlUi element properties
      # by Layout.apply). These are RESERVED for manual override; the default
      # RCSS positions are sufficient so we leave them nil/comment here.
    }.freeze

    # ------------------------------------------------------------------
    # Public API
    # ------------------------------------------------------------------

    # Load config from cwd. Returns a Hash with Symbol keys.
    # Missing / unreadable file → returns DEFAULTS.dup.
    def self.load
      cfg = DEFAULTS.dup
      path = config_path
      return cfg unless File.exist?(path)

      begin
        File.open(path, "r") do |f|
          f.each_line do |line|
            line = line.strip
            next if line.empty? || line.start_with?("#")

            key, val = line.split("=", 2)
            next unless key && val

            key_sym = key.strip.to_sym
            val_str = val.strip

            # Parse value type based on key context
            cfg[key_sym] = parse_value(key_sym, val_str)
          end
        end
      rescue => e
        # Unreadable file — stick with defaults
      end

      cfg
    end

    # Save settings hash to config file. Returns true on success.
    def self.save(settings)
      begin
        File.open(config_path, "w") do |f|
          f.puts "# Study Player config"
          f.puts "# Generated #{Time.now}" if respond_to?(:Time)
          settings.each do |key, val|
            val_s = format_value(val)
            f.puts "#{key}=#{val_s}"
          end
        end
        true
      rescue => e
        false
      end
    end

    # Return the full path to the config file.
    def self.config_path
      File.expand_path(CFG_FILE)
    end

    # ------------------------------------------------------------------
    # Value parsing / formatting
    # ------------------------------------------------------------------

    def self.parse_value(key, str)
      case key
      when :window_width, :window_height
        str.to_i
      when :volume, :font_scale
        str.to_f
      when :study_mode_default
        str == "true" || str == "1"
      else
        # For layout keys (numeric positions), return float
        f = str.to_f
        (f.to_i == f) ? f.to_i : f
      end
    end

    def self.format_value(val)
      case val
      when Float
        # Format with up to 2 decimal places, trim trailing zeros
        s = format("%.2f", val)
        s = s.sub(/\.?0+$/, "")
        s
      when TrueClass
        "true"
      when FalseClass
        "false"
      when Integer
        val.to_s
      else
        val.to_s
      end
    end
  end
end