2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
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.
19 /* the replay saver thing only works in the sdl version */
22 #include <SDL/SDL_mixer.h>
23 #include <glib/gi18n.h>
25 #include "framework/replaysaveractivity.hpp"
26 #include "framework/commands.hpp"
27 #include "sdl/IMG_savepng.h"
28 #include "cave/gamecontrol.hpp"
29 #include "cave/titleanimation.hpp"
30 #include "cave/caveset.hpp"
31 #include "misc/logger.hpp"
32 #include "misc/printf.hpp"
33 #include "settings.hpp"
34 #include "sound/sound.hpp"
37 void ReplaySaverActivity::SDLInmemoryScreen::set_title(char const *) {
41 void ReplaySaverActivity::SDLInmemoryScreen::configure_size() {
43 SDL_FreeSurface(surface
);
44 surface
= SDL_CreateRGBSurface(0, w
, h
, 32, 0, 0, 0, 0);
48 ReplaySaverActivity::SDLInmemoryScreen::~SDLInmemoryScreen() {
49 SDL_FreeSurface(surface
);
53 void ReplaySaverActivity::SDLInmemoryScreen::save(char const *filename
) {
54 IMG_SavePNG(filename
, surface
, 2); // 2 = not too much compression, but a bit faster than the default
58 ReplaySaverActivity::ReplaySaverActivity(App
*app
, CaveStored
*cave
, CaveReplay
*replay
, std::string
const &filename_prefix
)
61 filename_prefix(filename_prefix
),
62 game(GameControl::new_replay(app
->caveset
, cave
, replay
)),
66 cellrenderer(pf
, gd_theme
),
67 gamerenderer(pm
, cellrenderer
, fm
, *game
)
69 int cell_size
= cellrenderer
.get_cell_size();
70 pm
.set_size(cell_size
*GAME_RENDERER_SCREEN_SIZE_X
, cell_size
*GAME_RENDERER_SCREEN_SIZE_Y
);
71 gamerenderer
.set_show_replay_sign(false);
72 std::string wav_filename
= filename_prefix
+ ".wav";
73 wavfile
= fopen(wav_filename
.c_str(), "wb");
75 gd_critical(CPrintf("Cannot open %s for sound output") % wav_filename
);
76 app
->enqueue_command(new PopActivityCommand(app
));
79 fseek(wavfile
, 44, SEEK_SET
); /* 44bytes offset: start of data in a wav file */
85 void ReplaySaverActivity::shown_event() {
86 std::vector
<Pixmap
*> animation
= get_title_animation_pixmap(app
->caveset
->title_screen
, app
->caveset
->title_screen_scroll
, true, pf
);
87 pm
.blit(*animation
[0], 0, 0);
91 /* enable own timer and sound saver */
96 ReplaySaverActivity::~ReplaySaverActivity() {
97 Mix_SetPostMix(NULL
, NULL
); /* remove wav file saver */
98 uninstall_own_mixer();
101 /* write wav header, as now we now its final size. */
102 fseek(wavfile
, 0, SEEK_SET
);
107 i
+=fwrite("RIFF", 1, 4, wavfile
); /* "RIFF" */
108 out32
=GUINT32_TO_LE(wavlen
+36); i
+=fwrite(&out32
, 1, 4, wavfile
); /* 4 + 8+subchunk1size + 8+subchunk2size */
109 i
+=fwrite("WAVE", 1, 4, wavfile
); /* "WAVE" */
111 i
+=fwrite("fmt ", 1, 4, wavfile
); /* "fmt " */
112 out32
=GUINT32_TO_LE(16); i
+=fwrite(&out32
, 1, 4, wavfile
); /* fmt chunk size=16 bytes */
113 out16
=GUINT16_TO_LE(1); i
+=fwrite(&out16
, 1, 2, wavfile
); /* 1=pcm */
114 out16
=GUINT16_TO_LE(channels
); i
+=fwrite(&out16
, 1, 2, wavfile
);
115 out32
=GUINT32_TO_LE(frequency
); i
+=fwrite(&out32
, 1, 4, wavfile
);
116 out32
=GUINT32_TO_LE(frequency
*bits
/8*channels
); i
+=fwrite(&out32
, 1, 4, wavfile
); /* byterate */
117 out16
=GUINT16_TO_LE(bits
/8*channels
); i
+=fwrite(&out16
, 1, 2, wavfile
); /* blockalign */
118 out16
=GUINT16_TO_LE(bits
); i
+=fwrite(&out16
, 1, 2, wavfile
); /* bitspersample */
120 i
+=fwrite("data", 1, 4, wavfile
); /* "data" */
121 out32
=GUINT32_TO_LE(wavlen
); i
+=fwrite(&out32
, 1, 4, wavfile
); /* actual data length */
125 gd_critical("Could not write wav header to file!");
127 std::string message
= SPrintf(_("Saved %d video frames and %dMiB of audio data to %s_*.png and %s.wav.")) % (frame
+1) % (wavlen
/1048576) % filename_prefix
% filename_prefix
;
128 app
->show_message(_("Replay Saved"), message
);
131 gd_show_name_of_game
=saved_gd_show_name_of_game
;
132 gd_sound_set_music_volume();
133 gd_sound_set_chunk_volumes();
134 gd_music_play_random();
139 void ReplaySaverActivity::install_own_mixer() {
142 /* we setup mixing and other parameters for our own needs. */
143 saved_gd_sdl_sound
=gd_sound_enabled
;
144 saved_gd_sdl_44khz_mixing
=gd_sound_44khz_mixing
;
145 saved_gd_sdl_16bit_mixing
=gd_sound_16bit_mixing
;
146 saved_gd_sound_stereo
=gd_sound_stereo
;
147 const char *driver
=g_getenv("SDL_AUDIODRIVER");
151 /* and after tweaking settings to our own need, we can init sdl. */
152 gd_sound_enabled
=true;
153 gd_sound_44khz_mixing
=true;
154 gd_sound_16bit_mixing
=true;
155 gd_sound_stereo
=true;
156 // select the dummy driver, as it accepts any format, and we have control
157 g_setenv("SDL_AUDIODRIVER", "dummy", TRUE
);
158 gd_sound_init(44100/25); /* so the buffer will be 1/25th of a second. and we get our favourite 25hz interrupt from the mixer. */
160 /* query audio format from sdl */
162 Mix_QuerySpec(&frequency
, &format
, &channels
);
163 if (frequency
!=44100) /* something must be really going wrong. */
164 gd_critical("Cannot initialize mixer to 44100Hz mixing. The replay saver will not work correctly!");
167 case AUDIO_U8
: bits
=8; break;
168 case AUDIO_S8
: bits
=8; break;
169 case AUDIO_U16LSB
: bits
=16; break;
170 case AUDIO_S16LSB
: bits
=16; break;
171 case AUDIO_U16MSB
: bits
=16; break;
172 case AUDIO_S16MSB
: bits
=16; break;
174 g_assert_not_reached();
176 Mix_SetPostMix(mixfunc
, this);
180 void ReplaySaverActivity::uninstall_own_mixer()
185 gd_sound_enabled
=saved_gd_sdl_sound
;
186 gd_sound_44khz_mixing
=saved_gd_sdl_44khz_mixing
;
187 gd_sound_16bit_mixing
=saved_gd_sdl_16bit_mixing
;
188 gd_sound_stereo
=saved_gd_sound_stereo
;
190 if (saved_driver
!="")
191 g_setenv("SDL_AUDIODRIVER", saved_driver
.c_str(), TRUE
);
193 g_unsetenv("SDL_AUDIODRIVER");
199 /* this function saves the wav file, and also does the timing! */
200 void ReplaySaverActivity::mixfunc(void *udata
, Uint8
*stream
, int len
) {
201 ReplaySaverActivity
*rs
=static_cast<ReplaySaverActivity
*>(udata
);
205 if (fwrite(stream
, 1, len
, rs
->wavfile
)!=size_t(len
))
206 gd_critical("Cannot write to wav file!");
208 // push a user event -> to do the timing
210 ev
.type
=SDL_USEREVENT
+1;
217 void ReplaySaverActivity::redraw_event() {
220 app
->title_line("SAVING REPLAY");
221 app
->status_line("PLEASE WAIT");
223 // show it to the user.
224 // it is not in displayformat, but a small image - not that slow to draw.
225 // center coordinates
226 int x
=(app
->screen
->get_width()-pm
.get_width())/2;
227 int y
=(app
->screen
->get_height()-pm
.get_height())/2;
234 SDL_BlitSurface(pm
.get_surface(), 0, static_cast<SDLScreen
*>(app
->screen
)->get_surface(), &destr
);
240 void ReplaySaverActivity::timer2_event() {
241 /* iterate and see what happened */
242 /* no movement, no fire, no suicide, no restart, no pause, no fast movement.
243 * the gamerenderer takes the moves from the replay. if we would add a move or a fire
244 * press, the gamerenderer would switch to "continue replay" mode. */
247 GameRenderer::State state
= gamerenderer
.main_int(40, MV_STILL
, false, suicide
, restart
, false, false, false);
249 case GameRenderer::CaveLoaded
:
250 case GameRenderer::Iterated
:
251 case GameRenderer::Nothing
:
254 case GameRenderer::Stop
: /* game stopped, this could be a replay or a snapshot */
255 case GameRenderer::GameOver
: /* game over should not happen for a replay, but no problem */
256 app
->enqueue_command(new PopActivityCommand(app
));
260 /* before incrementing frame number, check if to save the frame to disk. */
261 char *filename
=g_strdup_printf("%s_%08d.png", filename_prefix
.c_str(), frame
);
269 #endif /* IFDEF HAVE_SDL */