1-- Solarus 1.0 to 1.1.
2-- Sprite data files change to a more readable syntax.
3-- The sprite features remain the same.
4
5local converter = {}
6
7-- Parses a sprite data file in 1.0 format and returns a table representing
8-- the sprite information.
9local function load_sprite(quest_path, sprite_id)
10
11  local input_file_name = quest_path .. "/data/sprites/" .. sprite_id .. ".dat"
12  local input_file, error_message = io.open(input_file_name)
13  if input_file == nil then
14    error("Cannot open old sprite file for reading: " .. error_message)
15  end
16
17  local sprite = {}
18  local line_number = 0
19  local animation = nil
20  local num_directions = 0
21  for line in input_file:lines() do
22
23    line_number = line_number + 1
24
25    if #line > 0 then  -- Skip empty lines.
26
27      if animation == nil then
28
29        -- The line is a new animation declaration.
30        animation = {}
31        animation.directions = {}
32        animation.name, animation.src_image, num_directions, animation.frame_delay, animation.frame_to_loop_on =
33            line:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s*$")
34
35        num_directions = tonumber(num_directions)
36        animation.frame_delay = tonumber(animation.frame_delay)
37        animation.frame_to_loop_on = tonumber(animation.frame_to_loop_on)
38        if animation.name == nil
39          or num_directions == nil
40          or animation.frame_delay == nil
41          or animation.frame_to_loop_on == nil then
42          error(input_file_name .. ": line " .. line_number .. ": Invalid animation declaration")
43        end
44
45        if animation.frame_delay == 0 then
46          -- nil or 0 means no frame end in Solarus 1.1
47          animation.frame_delay = nil
48        end
49
50        if animation.frame_to_loop_on == -1 then
51          -- nil means no loop in Solarus 1.1.
52          animation.frame_to_loop_on = nil
53        end
54
55      else
56        -- The line is a direction.
57        local direction = {}
58        direction.x, direction.y, direction.frame_width, direction.frame_height,
59        direction.origin_x, direction.origin_y, direction.num_frames, direction.num_columns =
60        line:match("^(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s+(%S+)%s*$")
61
62        direction.x = tonumber(direction.x)
63        direction.y = tonumber(direction.y)
64        direction.frame_width = tonumber(direction.frame_width)
65        direction.frame_height = tonumber(direction.frame_height)
66        direction.origin_x = tonumber(direction.origin_x)
67        direction.origin_y = tonumber(direction.origin_y)
68        direction.num_frames = tonumber(direction.num_frames)
69        direction.num_columns = tonumber(direction.num_columns)
70
71        if direction.x == nil
72          or direction.y == nil
73          or direction.frame_width == nil
74          or direction.frame_height == nil
75          or direction.origin_x == nil
76          or direction.origin_y == nil
77          or direction.num_frames == nil
78          or direction.num_columns == nil then
79          error(input_file_name .. ": line " .. line_number .. ": Invalid direction definition")
80        end
81
82        -- 0,0 is the default origin.
83        if direction.origin_x == 0 then
84          direction.origin_x = nil
85        end
86        if direction.origin_y == 0 then
87          direction.origin_y = nil
88        end
89
90        if direction.num_columns == direction.num_frames then
91          -- nil means num_frames columns.
92          direction.num_columns = nil
93        end
94
95        if direction.num_frames == 1 then
96          -- Default value.
97          direction.num_frames = nil
98        end
99
100        animation.directions[#animation.directions + 1] = direction
101
102        if #animation.directions == num_directions then
103          -- All directions of this animations were parsed.
104          sprite[#sprite + 1] = animation
105          animation = nil
106        end
107      end
108    end
109  end
110
111  input_file:close()
112
113  return sprite
114end
115
116function converter.convert(quest_path, sprite_id)
117
118  local sprite = load_sprite(quest_path, sprite_id)
119
120  local output_file_name = quest_path .. "/data/sprites/" .. sprite_id .. ".dat"
121  local output_file, error_message = io.open(output_file_name, "w")
122  if output_file == nil then
123    error("Cannot open new sprite file for writing: " .. error_message)
124  end
125
126  for _, animation in ipairs(sprite) do
127
128    -- Write the animation properties.
129    output_file:write("animation{\n");
130    output_file:write("  name = \"", animation.name, "\",\n")
131    output_file:write("  src_image = \"", animation.src_image, "\",\n")
132
133    if animation.frame_delay ~= nil then
134      output_file:write("  frame_delay = ", animation.frame_delay, ",\n")
135    end
136
137    if animation.frame_to_loop_on ~= nil then
138      output_file:write("  frame_to_loop_on = ", animation.frame_to_loop_on, ",\n")
139    end
140
141    output_file:write("  directions = {\n")
142
143    for _, direction in ipairs(animation.directions) do
144      output_file:write("    { x = ", direction.x,
145          ", y = ", direction.y,
146          ", frame_width = ", direction.frame_width,
147          ", frame_height = ", direction.frame_height)
148      if direction.origin_x ~= nil then
149        output_file:write(", origin_x = ", direction.origin_x)
150      end
151      if direction.origin_y ~= nil then
152        output_file:write(", origin_y = ", direction.origin_y)
153      end
154      if direction.num_frames ~= nil then
155        output_file:write(", num_frames = ", direction.num_frames)
156      end
157      if direction.num_columns ~= nil then
158        output_file:write(", num_columns = ", direction.num_columns)
159      end
160      output_file:write(" },\n")
161    end
162
163    output_file:write("  },\n")
164    output_file:write("}\n\n")
165  end
166
167  output_file:close()
168end
169
170return converter
171
172