2 * Copyright (c) 2007, 2008, 2009, Czirkos Zoltan <cirix@fw.hu>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <glib/gi18n.h>
24 #include "caveobject.h"
26 #include "c64import.h"
29 static int crli_steel_code
=0x38; /* magic value: the code of steel wall in crli */
32 static int element_to_crli(GdElement e
, GHashTable
*unknown
)
37 /* hack: there is no separate horizontal and vertical growing wall in crli. */
38 /* only the switch can determine the direction. */
39 if (e
==O_V_EXPANDING_WALL
)
42 /* 128 is the number of elements in gd_crazylight_import_table */
44 if (gd_crazylight_import_table
[i
]==e
) {
46 break; /* if found, exit loop */
48 if (code
==-1 && (e
&SCANNED
)) {
51 /* try once again, without the "scanned" flag, as the element we use may not have a delayed state originally */
53 if (gd_crazylight_import_table
[i
]==e
) {
55 break; /* if found, exit loop */
60 g_assert(gd_crazylight_import_table
[crli_steel_code
]==O_STEEL
);
62 if (g_hash_table_lookup(unknown
, GINT_TO_POINTER(code
))==NULL
) {
63 g_warning("the element '%s' can not be saved in crli, saving steel wall instead", gd_elements
[e
].name
);
64 g_hash_table_insert(unknown
, GINT_TO_POINTER(e
), GINT_TO_POINTER(e
));
74 crli_export(GdCave
*to_convert
, const int level
, guint8
*compressed
)
85 gboolean has_horizontal
, has_vertical
;
87 /* render cave with seed=0 */
88 cave
=gd_cave_new_rendered(to_convert
, level
, 0);
89 gd_error_set_context(to_convert
->name
);
90 unknown
=g_hash_table_new(g_direct_hash
, g_direct_equal
); /* hash table to remember unconvertable elements */
94 g_warning("crli only supports line shifting map wraparound");
95 if (!cave
->pal_timing
|| cave
->scheduling
!=GD_SCHEDULING_PLCK
)
96 g_warning("only applicable timing settings for crli are pal timing=true, scheduling=plck");
97 if (cave
->amoeba_timer_started_immediately
)
98 g_warning("crli amoeba timer is only started when the amoeba is let free!");
99 if (!cave
->amoeba_timer_wait_for_hatching
) {
100 cave
->amoeba_time
+=2;
101 g_message("crli amoeba timer waits for hatching; added 2 seconds for correction");
103 if (!cave
->voodoo_dies_by_stone
|| !cave
->voodoo_collects_diamonds
|| cave
->voodoo_disappear_in_explosion
)
104 g_warning("crli voodoo dies by stone hit, can collect diamonds and can't be destroyed");
105 if (cave
->short_explosions
)
106 g_warning("crli explosions are slower than original");
107 if (!cave
->magic_timer_wait_for_hatching
) {
108 cave
->magic_wall_time
+=2;
109 g_message("crli magic wall timer waits for hatching; added 2 seconds for correction");
111 if (!cave
->slime_predictable
)
112 g_warning("crli only supports predictable slime");
114 /* fill data bytes with some defaults */
115 g_assert(gd_crazylight_import_table
[crli_steel_code
]==O_STEEL
); /* check magic value */
116 for (i
=0; i
<40*22; i
++) /* fill map with steel wall */
117 output
[i
]=crli_steel_code
;
118 for (i
=40*22; i
<G_N_ELEMENTS(output
); i
++) /* fill properties with zero */
121 /* check cave sizes */
122 if (cave
->w
!=40 || cave
->h
!=22)
123 g_warning("cave sizes out of range, should be 40x22 instead of %dx%d", cave
->w
, cave
->h
);
124 gd_cave_correct_visible_size(cave
);
125 if (cave
->intermission
) { /* visible size for intermissions */
126 if (cave
->x1
!=0 || cave
->y1
!=0 || cave
->x2
!=19 || cave
->y2
!=11)
127 g_warning("for intermissions, the upper left 20x12 elements should be visible");
128 } else { /* visible size for normal caves */
129 if (cave
->x1
!=0 || cave
->y1
!=0 || cave
->x2
!=cave
->w
-1 || cave
->y2
!=cave
->h
-1)
130 g_warning("for normal caves, the whole cave should be visible");
134 has_horizontal
=FALSE
;
136 for (y
=0; y
<cave
->h
&& y
<22; y
++)
137 for (x
=0; x
<cave
->w
&& x
<40; x
++) {
138 output
[y
*40+x
]=element_to_crli(cave
->map
[y
][x
], unknown
);
140 if (cave
->map
[y
][x
]==O_H_EXPANDING_WALL
)
142 if (cave
->map
[y
][x
]==O_V_EXPANDING_WALL
)
146 output
[0x370]=cave
->level_time
[level
]/100%10;
147 output
[0x371]=cave
->level_time
[level
]/10%10;
148 output
[0x372]=cave
->level_time
[level
]/1%10;
150 output
[0x373]=cave
->level_diamonds
[level
]/100%10;
151 output
[0x374]=cave
->level_diamonds
[level
]/10%10;
152 output
[0x375]=cave
->level_diamonds
[level
]/1%10;
154 output
[0x376]=cave
->extra_diamond_value
/100%10;
155 output
[0x377]=cave
->extra_diamond_value
/10%10;
156 output
[0x378]=cave
->extra_diamond_value
/1%10;
158 output
[0x379]=cave
->diamond_value
/100%10;
159 output
[0x37a]=cave
->diamond_value
/10%10;
160 output
[0x37b]=cave
->diamond_value
/1%10;
162 output
[0x37c]=cave
->amoeba_time
/256;
163 output
[0x37d]=cave
->amoeba_time
%256;
165 output
[0x37e]=cave
->magic_wall_time
/256;
166 output
[0x37f]=cave
->magic_wall_time
%256;
168 if (cave
->creatures_direction_auto_change_time
) {
170 output
[0x381]=cave
->creatures_direction_auto_change_time
;
173 prob
=(int)(4.0/cave
->amoeba_growth_prob
-1+0.5);
174 if (prob
!=0 && prob
!=1 && prob
!=3 && prob
!=7 && prob
!=15 && prob
!=31 && prob
!=63 && prob
!=127)
175 g_warning("cannot precisely export amoeba slow growth probability, %d", prob
);
177 prob
=(int)(4.0/cave
->amoeba_fast_growth_prob
-1+0.5);
178 if (prob
!=0 && prob
!=1 && prob
!=3 && prob
!=7 && prob
!=15 && prob
!=31 && prob
!=63 && prob
!=127)
179 g_warning("cannot precisely export amoeba fast growth probability");
182 output
[0x384]=gd_color_get_c64_index_try(cave
->colorb
);
183 output
[0x385]=gd_color_get_c64_index_try(cave
->color0
);
184 output
[0x386]=gd_color_get_c64_index_try(cave
->color1
);
185 output
[0x387]=gd_color_get_c64_index_try(cave
->color2
);
186 x
=gd_color_get_c64_index_try(cave
->color3
);
188 g_warning("allowed colors for color3 are Black..Yellow, but it is %d", x
);
191 output
[0x389]=cave
->intermission
?1:0;
192 output
[0x38a]=cave
->level_ckdelay
[level
];
194 output
[0x38b]=cave
->slime_permeability_c64
;
195 output
[0x38c]=0; /* always "normal" intermission, scrolling intermission is said to be buggy */
196 output
[0x38d]=0xf1; /* magic wall sound on */
198 /* if changed direction, we swap the flags here. */
199 /* effects are already converted by element_to_crli */
200 if (cave
->expanding_wall_changed
) {
204 has_horizontal
=has_vertical
;
208 if (has_vertical
&& !has_horizontal
)
210 if (has_horizontal
&& has_vertical
)
211 g_warning("a crli map cannot contain horizontal and vertical growing walls at the same time");
212 output
[0x38f]=cave
->creatures_backwards
?0x2d:0x2c;
214 output
[0x390]=cave
->amoeba_max_count
/256;
215 output
[0x391]=cave
->amoeba_max_count
%256;
217 output
[0x392]=cave
->time_bonus
;
218 output
[0x393]=cave
->time_penalty
;
219 output
[0x394]=cave
->biter_delay_frame
;
220 output
[0x395]=cave
->magic_wall_stops_amoeba
?0:1; /* inverted! */
222 output
[0x396]=element_to_crli(cave
->bomb_explosion_effect
|SCANNED
, unknown
);
223 output
[0x397]=element_to_crli(cave
->explosion_effect
|SCANNED
, unknown
);
224 if (cave
->stone_falling_effect
!=O_STONE_F
)
225 g_warning("crli does not support 'falling stone to' effect");
226 output
[0x398]=element_to_crli(cave
->stone_bouncing_effect
|SCANNED
, unknown
);
227 output
[0x399]=element_to_crli(cave
->diamond_birth_effect
|SCANNED
, unknown
);
228 output
[0x39a]=element_to_crli(cave
->magic_diamond_to
|SCANNED
, unknown
);
229 if (cave
->diamond_bouncing_effect
!=O_DIAMOND
)
230 g_warning("crli does not support 'bouncing diamond turns to' effect");
231 output
[0x39b]=element_to_crli(cave
->bladder_converts_by
, unknown
);
232 output
[0x39c]=element_to_crli(cave
->diamond_falling_effect
|SCANNED
, unknown
);
233 if (cave
->diamond_bouncing_effect
!=O_DIAMOND
)
234 g_warning("crli does not support 'bouncing diamond turns to' effect");
235 output
[0x39d]=element_to_crli(cave
->biter_eat
, unknown
);
236 output
[0x39e]=element_to_crli(cave
->slime_eats_1
, unknown
);
237 if (element_to_crli(cave
->slime_eats_1
, unknown
)+3!=element_to_crli(cave
->slime_converts_1
|SCANNED
, unknown
))
238 g_warning("cannot convert slime setting: %s to %s", gd_elements
[cave
->slime_eats_1
].name
, gd_elements
[cave
->slime_converts_1
].name
);
239 output
[0x39f]=element_to_crli(cave
->slime_eats_2
, unknown
);
240 if (element_to_crli(cave
->slime_eats_2
, unknown
)+3!=element_to_crli(cave
->slime_converts_2
|SCANNED
, unknown
))
241 g_warning("cannot convert slime setting: %s to %s", gd_elements
[cave
->slime_eats_2
].name
, gd_elements
[cave
->slime_converts_2
].name
);
243 output
[0x3a0]='V'; /* version number */
248 output
[0x3a4]=cave
->diagonal_movements
?1:0;
249 output
[0x3a6]=element_to_crli(cave
->amoeba_too_big_effect
|SCANNED
, unknown
);
250 output
[0x3a7]=element_to_crli(cave
->amoeba_enclosed_effect
|SCANNED
, unknown
);
251 output
[0x3a8]=cave
->acid_spread_ratio
*255.0;
252 output
[0x3a9]=element_to_crli(cave
->acid_eats_this
, unknown
);
253 output
[0x3ab]=element_to_crli(cave
->expanding_wall_looks_like
, unknown
);
254 output
[0x3ac]=element_to_crli(cave
->dirt_looks_like
, unknown
);
260 while (i
<G_N_ELEMENTS(output
)) {
261 if (output
[i
]==prev
) { /* same as previous */
262 /* if it would be too much for the length to be fit in a byte, write now */
264 compressed
[out
++]=0xbf;
265 compressed
[out
++]=prev
; /* which is == 0xbf */
266 compressed
[out
++]=count
;
272 /* not the same as the previous ones, so write out those */
273 if (count
==1 && prev
!=0xbf) /* output previous character */
274 compressed
[out
++]=prev
;
275 else if (count
==1 && prev
==0xbf) { /* output previous character, but it is accidentally the escape code */
276 compressed
[out
++]=0xbf;
277 compressed
[out
++]=prev
; /* which is == 0xbf */
279 } else if (count
==2) {
280 /* count=2 is not written as escape, byte, count, as it would make it longer */
282 /* if the byte to write is not the escape byte */
283 compressed
[out
++]=prev
; /* which is == 0xbf */
284 compressed
[out
++]=prev
; /* which is == 0xbf */
286 /* we have two escape bytes */
287 compressed
[out
++]=0xbf;
288 compressed
[out
++]=prev
; /* which is == 0xbf */
293 compressed
[out
++]=0xbf;
294 compressed
[out
++]=prev
;
295 compressed
[out
++]=count
;
298 /* and process this one. */
303 i
++; /* next byte to compress */
305 /* process remaining bytes; always write them as compressed with escape byte */
306 compressed
[out
++]=0xbf;
307 compressed
[out
++]=prev
;
308 compressed
[out
++]=count
;
311 g_hash_table_destroy(unknown
);
313 /* return number of bytes */
318 gd_export_cave_to_crli_cavefile(GdCave
*cave
, int level
, const char *filename
)
329 size
=crli_export(cave
, level
, data
+5);
330 gd_error_set_context(NULL
);
331 if (!g_file_set_contents(filename
, (gchar
*)data
, size
+5, &error
)) {
332 /* could not save properly */
333 g_warning("%s: %s", filename
, error
->message
);
340 gd_export_cave_list_to_crli_cavepack(GList
*caveset
, int level
, const char *filename
)
343 const int start
=0x6ffa;
344 guint8 out
[0xcc00-start
+1024]; /* max number of cavepack + 1024 so dont worry about buffer overrun :P */
352 /* cavepack version number */
353 out
[0x6ffc - start
]='V';
354 out
[0x6ffd - start
]='3';
355 out
[0x6ffe - start
]='.';
356 out
[0x6fff - start
]='0';
358 for (i
=0; i
<48; i
++) {
359 out
[0x7000-start
+i
]=0xff;
360 out
[0x7030-start
+i
]=0xff;
361 out
[0x7060-start
+i
]=0xff; /* no cave present */
363 out
[0x7090-start
]=0xff;
364 out
[0x7091-start
]=0x60;
365 out
[0x7092-start
]=0x60;
366 out
[0x7093-start
]=0x60;
367 pos
=0x70a2; /* first available byte; before that we have space for the name */
369 for (iter
=caveset
, i
=0; iter
!=NULL
; iter
=iter
->next
, i
++) {
370 GdCave
*cave
=iter
->data
;
378 g_warning("maximum of 48 caves in a crli cavepack");
382 bytes
=crli_export(cave
, level
, out
+pos
-start
);
384 if (pos
+bytes
>0xcbff) {
385 g_warning("run out of data space, not writing this cave");
389 out
[0x7000-start
+i
]=pos
%256; /* lsb */
390 out
[0x7030-start
+i
]=pos
/256; /* msb */
391 out
[0x7060-start
+i
]=cave
->selectable
?0:1; /* selection table */
394 for (c
=0; c
<14; c
++) /* fill with space */
395 out
[pos
-start
-14+c
]=0x20;
400 ch
=g_utf8_get_char(namepos
);
401 while (c
<14 && ch
!=0) {
405 out
[pos
-start
-14+c
]=' '; /* space is default, also for unknown characters */
406 if (ch
==' ') /* treat space as different, as gd_bd_internal_chars has lots of spaces at unknown character positions */
410 for (j
=0; j
<strlen(gd_bd_internal_chars
); j
++)
411 if (gd_bd_internal_chars
[j
]==g_ascii_toupper(ch
)) { /* search for the character */
412 out
[pos
-start
-14+c
]=j
;
421 namepos
=g_utf8_next_char(namepos
);
422 ch
=g_utf8_get_char(namepos
);
425 g_warning("couldn't export cave name properly");
427 pos
+=bytes
+14; /* jump number of bytes + place for next cave name */
429 pos
-=14; /* subtract 14 for place we left for next cave name, but there was no more cave */
431 if (!g_file_set_contents(filename
, (gchar
*)out
, pos
-start
, &error
)) {
432 /* could not save properly */
433 g_warning("%s: %s", filename
, error
->message
);
440 string_printf_markup(GString
*string
, const char *format
, ...)
445 va_start(ap
, format
);
446 text
=g_markup_vprintf_escaped(format
, ap
);
448 g_string_append(string
, text
);
452 /* save caveset as html gallery.
454 window: show progress bar in a small dialog, transient for this window, if not NULL
457 gd_save_html(char *htmlname
, GtkWidget
*window
)
459 char *pngbasename
; /* used as a base for img src= tags */
460 char *pngoutbasename
; /* used as a base name for png output files */
462 GtkWidget
*dialog
=NULL
, *progress
=NULL
;
466 contents
=g_string_sized_new(20000);
469 dialog
=gtk_dialog_new ();
470 gtk_window_set_transient_for(GTK_WINDOW(dialog
), GTK_WINDOW(window
));
471 progress
=gtk_progress_bar_new ();
472 gtk_window_set_title (GTK_WINDOW (dialog
), _("Saving HTML gallery"));
473 gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), progress
);
474 gtk_widget_show_all (dialog
);
477 if (g_str_has_suffix (htmlname
, ".html")) {
478 /* has html extension */
479 pngoutbasename
=g_strdup (htmlname
);
480 *g_strrstr(pngoutbasename
, ".html")=0;
483 /* has no html extension */
484 pngoutbasename
=g_strdup(htmlname
);
485 htmlname
=g_strdup_printf("%s.html", pngoutbasename
);
487 pngbasename
=g_path_get_basename(pngoutbasename
);
489 g_string_append(contents
, "<HTML>\n");
490 g_string_append(contents
, "<HEAD>\n");
491 string_printf_markup(contents
, "<TITLE>%s</TITLE>\n", gd_caveset_data
->name
);
492 g_string_append(contents
, "<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\">\n");
493 if (gd_html_stylesheet_filename
)
494 g_string_append_printf(contents
, "<link rel=\"stylesheet\" href=\"%s\">\n", gd_html_stylesheet_filename
);
495 if (gd_html_favicon_filename
)
496 g_string_append_printf(contents
, "<link rel=\"shortcut icon\" href=\"%s\">\n", gd_html_favicon_filename
);
497 g_string_append(contents
, "</HEAD>\n\n");
499 g_string_append(contents
, "<BODY>\n");
501 string_printf_markup(contents
, "<H1>%s</H1>\n", gd_caveset_data
->name
);
502 /* if the game has its own title screen */
503 if (gd_caveset_data
->title_screen
->len
!=0) {
504 GdkPixbuf
*title_image
;
507 /* create the title image and save it */
508 title_image
=gd_create_title_image();
509 pngname
=g_strdup_printf("%s_%03d.png", pngoutbasename
, 0); /* it is the "zeroth" image */
510 gdk_pixbuf_save(title_image
, pngname
, "png", &error
, "compression", "9", NULL
);
512 g_warning("%s", error
->message
);
518 g_string_append_printf(contents
, "<IMAGE SRC=\"%s_%03d.png\" WIDTH=\"%d\" HEIGHT=\"%d\">\n", pngbasename
, 0, gdk_pixbuf_get_width(title_image
), gdk_pixbuf_get_height (title_image
));
519 g_string_append_printf(contents
, "<BR>\n");
521 g_object_unref(title_image
);
523 g_string_append(contents
, "<TABLE>\n");
524 string_printf_markup(contents
, "<TR><TH>%s<TD>%d\n", _("Caves"), gd_caveset_count());
525 if (!g_str_equal(gd_caveset_data
->author
, ""))
526 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Author"), gd_caveset_data
->author
);
527 if (!g_str_equal(gd_caveset_data
->description
, ""))
528 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Description"), gd_caveset_data
->description
);
529 if (!g_str_equal(gd_caveset_data
->www
, ""))
530 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("WWW"), gd_caveset_data
->www
);
531 if (!g_str_equal(gd_caveset_data
->remark
->str
, ""))
532 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Remark"), gd_caveset_data
->remark
->str
);
533 if (!g_str_equal(gd_caveset_data
->story
->str
, ""))
534 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Story"), gd_caveset_data
->story
->str
);
535 g_string_append(contents
, "</TABLE>\n");
537 /* cave names, descriptions, hrefs */
538 g_string_append(contents
, "<DL>\n");
539 for (i
=0; i
<gd_caveset_count(); i
++) {
542 cave
=gd_return_nth_cave(i
);
543 string_printf_markup(contents
, "<DT><A HREF=\"#cave%03d\">%s</A></DT>\n<DD>%s</DD>\n", i
+1, cave
->name
, cave
->description
);
545 g_string_append(contents
, "</DL>\n\n");
547 for (i
=0; i
<gd_caveset_count(); i
++) {
552 gboolean has_amoeba
=FALSE
, has_magic
=FALSE
;
557 gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR (progress
), (float) i
/gd_caveset_count());
558 text
=g_strdup_printf("%d/%d", i
, gd_caveset_count());
559 gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress
), text
);
561 gdk_window_process_all_updates();
564 /* rendering cave for png: seed=0 */
565 cave
=gd_cave_new_from_caveset(i
, 0, 0);
566 pixbuf
=gd_drawcave_to_pixbuf(cave
, 0, 0, TRUE
, FALSE
);
568 /* check cave to see if we have amoeba or magic wall. properties will be shown in html, if so. */
569 for (y
=0; y
<cave
->h
; y
++)
570 for (x
=0; x
<cave
->w
; x
++)
571 if (gd_cave_get_rc(cave
, x
, y
)==O_AMOEBA
) {
575 for (y
=0; y
<cave
->h
; y
++)
576 for (x
=0; x
<cave
->w
; x
++)
577 if (gd_cave_get_rc(cave
, x
, y
)==O_MAGIC_WALL
) {
583 pngname
=g_strdup_printf("%s_%03d.png", pngoutbasename
, i
+ 1);
584 gdk_pixbuf_save(pixbuf
, pngname
, "png", &error
, "compression", "9", NULL
);
586 g_warning("%s", error
->message
);
593 string_printf_markup(contents
, "<A NAME=\"cave%03d\"></A>\n<H2>%s</H2>\n", i
+1, cave
->name
);
594 g_string_append_printf(contents
, "<IMAGE SRC=\"%s_%03d.png\" WIDTH=\"%d\" HEIGHT=\"%d\">\n", pngbasename
, i
+1, gdk_pixbuf_get_width (pixbuf
), gdk_pixbuf_get_height (pixbuf
));
595 g_string_append(contents
, "<BR>\n");
596 g_string_append(contents
, "<TABLE>\n");
597 if (!g_str_equal(cave
->author
, ""))
598 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Author"), cave
->author
);
599 if (!g_str_equal(cave
->description
, ""))
600 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Description"), cave
->description
);
601 if (!g_str_equal(cave
->remark
->str
, "")) {
602 /* we must split the story into lines, and join them with html <br> */
607 escaped
=g_markup_escape_text(cave
->remark
->str
, -1);
608 spl
=g_strsplit_set(escaped
, "\n", -1);
610 /* maintain line breaks */
611 join
=g_strjoinv("<BR>\n", spl
);
613 g_string_append_printf(contents
, "<TR><TH>%s<TD>%s\n", _("Remark"), join
); /* string already escaped! */
616 if (!g_str_equal(cave
->story
->str
, "")) {
617 /* we must split the story into lines, and join them with html <br> */
622 escaped
=g_markup_escape_text(cave
->story
->str
, -1);
623 spl
=g_strsplit_set(escaped
, "\n", -1);
625 /* maintain line breaks */
626 join
=g_strjoinv("<BR>\n", spl
);
628 g_string_append_printf(contents
, "<TR><TH>%s<TD>%s\n", _("Story"), join
); /* string already escaped! */
631 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Type"), cave
->intermission
? _("Intermission") : _("Normal cave"));
632 string_printf_markup(contents
, "<TR><TH>%s<TD>%s\n", _("Selectable as start"), cave
->selectable
? _("Yes") : _("No"));
633 string_printf_markup(contents
, "<TR><TH>%s<TD>%d\n", _("Diamonds needed"), cave
->diamonds_needed
);
634 string_printf_markup(contents
, "<TR><TH>%s<TD>%d\n", _("Diamond value"), cave
->diamond_value
);
635 string_printf_markup(contents
, "<TR><TH>%s<TD>%d\n", _("Extra diamond value"), cave
->extra_diamond_value
);
636 string_printf_markup(contents
, "<TR><TH>%s<TD>%d\n", _("Time (s)"), cave
->time
);
638 string_printf_markup(contents
, "<TR><TH>%s<TD>%d, %d\n", _("Amoeba threshold and time (s)"), cave
->amoeba_max_count
, cave
->amoeba_time
);
640 string_printf_markup(contents
, "<TR><TH>%s<TD>%d\n", _("Magic wall milling time (s)"), cave
->magic_wall_time
);
641 g_string_append(contents
, "</TABLE>\n");
643 g_string_append(contents
, "\n");
646 g_object_unref (pixbuf
);
648 g_string_append(contents
, "</BODY>\n");
649 g_string_append(contents
, "</HTML>\n");
650 g_free (pngoutbasename
);
651 g_free (pngbasename
);
653 if (!g_file_set_contents (htmlname
, contents
->str
, contents
->len
, &error
)) {
654 /* could not save properly */
655 g_warning("%s", error
->message
);
658 g_string_free(contents
, TRUE
);
661 gtk_widget_destroy(dialog
);