2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
19 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
20 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 /* the replay saver thing only works in the sdl version */
29 #include <SDL/SDL_mixer.h>
30 #include <glib/gi18n.h>
32 #include "framework/replaysaveractivity.hpp"
33 #include "framework/app.hpp"
34 #include "framework/commands.hpp"
35 #include "sdl/IMG_savepng.h"
36 #include "sound/sound.hpp"
37 #include "cave/gamerender.hpp"
38 #include "cave/gamecontrol.hpp"
39 #include "cave/titleanimation.hpp"
40 #include "cave/caveset.hpp"
41 #include "misc/logger.hpp"
42 #include "settings.hpp"
43 #include "sdl/sdlpixbuf.hpp"
45 class SDLInmemoryPixmap
: public Pixmap
{
49 SDLInmemoryPixmap(const SDLInmemoryPixmap
&); // copy ctor not implemented
50 SDLInmemoryPixmap
&operator=(const SDLInmemoryPixmap
&); // operator= not implemented
53 SDLInmemoryPixmap(SDL_Surface
*surface_
) : surface(surface_
) {}
54 ~SDLInmemoryPixmap() {
55 SDL_FreeSurface(surface
);
58 virtual int get_width() const {
61 virtual int get_height() const {
67 Pixmap
*SDLInmemoryScreen::create_pixmap_from_pixbuf(Pixbuf
const &pb
, bool keep_alpha
) const {
68 SDL_Surface
*to_copy
= static_cast<SDLPixbuf
const &>(pb
).get_surface();
69 SDL_Surface
*newsurface
= SDL_CreateRGBSurface(keep_alpha
? SDL_SRCALPHA
: 0, to_copy
->w
, to_copy
->h
, 32,
70 surface
->format
->Rmask
, surface
->format
->Gmask
, surface
->format
->Bmask
, surface
->format
->Amask
);
71 SDL_SetAlpha(to_copy
, 0, SDL_ALPHA_OPAQUE
);
72 SDL_BlitSurface(to_copy
, NULL
, newsurface
, NULL
);
73 return new SDLInmemoryPixmap(newsurface
);
77 void SDLInmemoryScreen::set_title(char const *) {
78 /* do nothing, not a real window */
82 void SDLInmemoryScreen::configure_size() {
84 SDL_FreeSurface(surface
);
85 surface
= SDL_CreateRGBSurface(SDL_SRCALPHA
, w
, h
, 32, Pixbuf::rmask
, Pixbuf::gmask
, Pixbuf::bmask
, Pixbuf::amask
);
86 Uint32 col
= SDL_MapRGBA(surface
->format
, 0, 0, 0, SDL_ALPHA_OPAQUE
);
87 SDL_FillRect(surface
, NULL
, col
);
91 SDLInmemoryScreen::~SDLInmemoryScreen() {
92 SDL_FreeSurface(surface
);
96 void SDLInmemoryScreen::save(char const *filename
) {
97 IMG_SavePNG(filename
, surface
, 2); // 2 = not too much compression, but a bit faster than the default
101 Pixbuf
const *SDLInmemoryScreen::create_pixbuf_screenshot() const {
102 SDL_Surface
*sub
= SDL_CreateRGBSurfaceFrom(surface
->pixels
,
103 w
, h
, 32, surface
->pitch
, surface
->format
->Rmask
, surface
->format
->Gmask
, surface
->format
->Bmask
, 0);
104 return new SDLPixbuf(sub
);
108 ReplaySaverActivity::ReplaySaverActivity(App
*app
, CaveStored
*cave
, CaveReplay
*replay
, std::string
const &filename_prefix
)
111 filename_prefix(filename_prefix
),
112 game(GameControl::new_replay(app
->caveset
, cave
, replay
)),
116 cellrenderer(pm
, gd_theme
),
117 gamerenderer(pm
, cellrenderer
, fm
, *game
) {
118 int cell_size
= cellrenderer
.get_cell_size();
119 pm
.set_size(cell_size
* GAME_RENDERER_SCREEN_SIZE_X
, cell_size
* GAME_RENDERER_SCREEN_SIZE_Y
, false);
120 gamerenderer
.screen_initialized();
121 gamerenderer
.set_show_replay_sign(false);
122 std::string wav_filename
= filename_prefix
+ ".wav";
123 wavfile
= fopen(wav_filename
.c_str(), "wb");
125 gd_critical(CPrintf("Cannot open %s for sound output") % wav_filename
);
126 app
->enqueue_command(new PopActivityCommand(app
));
129 fseek(wavfile
, 44, SEEK_SET
); /* 44bytes offset: start of data in a wav file */
132 /* save settings and install own settings */
133 saved_gd_show_name_of_game
= gd_show_name_of_game
;
134 gd_show_name_of_game
= true;
138 void ReplaySaverActivity::shown_event() {
139 std::vector
<Pixmap
*> animation
= get_title_animation_pixmap(app
->caveset
->title_screen
, app
->caveset
->title_screen_scroll
, true, pm
, pf
);
140 pm
.blit(*animation
[0], 0, 0);
144 /* enable own timer and sound saver */
149 ReplaySaverActivity::~ReplaySaverActivity() {
150 Mix_SetPostMix(NULL
, NULL
); /* remove wav file saver */
151 uninstall_own_mixer();
154 /* write wav header, as now we now its final size. */
155 fseek(wavfile
, 0, SEEK_SET
);
160 i
+= fwrite("RIFF", 1, 4, wavfile
); /* "RIFF" */
161 out32
= GUINT32_TO_LE(wavlen
+ 36);
162 i
+= fwrite(&out32
, 1, 4, wavfile
); /* 4 + 8+subchunk1size + 8+subchunk2size */
163 i
+= fwrite("WAVE", 1, 4, wavfile
); /* "WAVE" */
165 i
+= fwrite("fmt ", 1, 4, wavfile
); /* "fmt " */
166 out32
= GUINT32_TO_LE(16);
167 i
+= fwrite(&out32
, 1, 4, wavfile
); /* fmt chunk size=16 bytes */
168 out16
= GUINT16_TO_LE(1);
169 i
+= fwrite(&out16
, 1, 2, wavfile
); /* 1=pcm */
170 out16
= GUINT16_TO_LE(channels
);
171 i
+= fwrite(&out16
, 1, 2, wavfile
);
172 out32
= GUINT32_TO_LE(frequency
);
173 i
+= fwrite(&out32
, 1, 4, wavfile
);
174 out32
= GUINT32_TO_LE(frequency
* bits
/ 8 * channels
);
175 i
+= fwrite(&out32
, 1, 4, wavfile
); /* byterate */
176 out16
= GUINT16_TO_LE(bits
/ 8 * channels
);
177 i
+= fwrite(&out16
, 1, 2, wavfile
); /* blockalign */
178 out16
= GUINT16_TO_LE(bits
);
179 i
+= fwrite(&out16
, 1, 2, wavfile
); /* bitspersample */
181 i
+= fwrite("data", 1, 4, wavfile
); /* "data" */
182 out32
= GUINT32_TO_LE(wavlen
);
183 i
+= fwrite(&out32
, 1, 4, wavfile
); /* actual data length */
187 gd_critical("Could not write wav header to file!");
189 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
;
190 app
->show_message(_("Replay Saved"), message
);
193 gd_show_name_of_game
= saved_gd_show_name_of_game
;
194 gd_sound_set_music_volume();
195 gd_sound_set_chunk_volumes();
196 gd_music_play_random();
201 void ReplaySaverActivity::install_own_mixer() {
204 /* we setup mixing and other parameters for our own needs. */
205 saved_gd_sdl_sound
= gd_sound_enabled
;
206 saved_gd_sdl_44khz_mixing
= gd_sound_44khz_mixing
;
207 saved_gd_sdl_16bit_mixing
= gd_sound_16bit_mixing
;
208 saved_gd_sound_stereo
= gd_sound_stereo
;
209 const char *driver
= g_getenv("SDL_AUDIODRIVER");
211 saved_driver
= driver
;
213 /* and after tweaking settings to our own need, we can init sdl. */
214 gd_sound_enabled
= true;
215 gd_sound_44khz_mixing
= true;
216 gd_sound_16bit_mixing
= true;
217 gd_sound_stereo
= true;
218 // select the dummy driver, as it accepts any format, and we have control
219 g_setenv("SDL_AUDIODRIVER", "dummy", TRUE
);
220 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. */
222 /* query audio format from sdl */
224 Mix_QuerySpec(&frequency
, &format
, &channels
);
225 if (frequency
!= 44100) /* something must be really going wrong. */
226 gd_critical("Cannot initialize mixer to 44100Hz mixing. The replay saver will not work correctly!");
240 g_assert_not_reached();
242 Mix_SetPostMix(mixfunc
, this);
246 void ReplaySaverActivity::uninstall_own_mixer() {
250 gd_sound_enabled
= saved_gd_sdl_sound
;
251 gd_sound_44khz_mixing
= saved_gd_sdl_44khz_mixing
;
252 gd_sound_16bit_mixing
= saved_gd_sdl_16bit_mixing
;
253 gd_sound_stereo
= saved_gd_sound_stereo
;
255 if (saved_driver
!= "")
256 g_setenv("SDL_AUDIODRIVER", saved_driver
.c_str(), TRUE
);
258 g_unsetenv("SDL_AUDIODRIVER");
264 /* this function saves the wav file, and also does the timing! */
265 void ReplaySaverActivity::mixfunc(void *udata
, Uint8
*stream
, int len
) {
266 ReplaySaverActivity
*rs
= static_cast<ReplaySaverActivity
*>(udata
);
270 if (fwrite(stream
, 1, len
, rs
->wavfile
) != size_t(len
))
271 gd_critical("Cannot write to wav file!");
273 // push a user event -> to do the timing
275 ev
.type
= SDL_USEREVENT
+ 1;
283 void ReplaySaverActivity::redraw_event(bool full
) const {
286 // TRANSLATORS: Title line capitalization in English
287 app
->title_line(_("Saving Replay"));
288 app
->status_line(_("Please wait"));
290 // show it to the user.
291 // it is not in displayformat, but a small image - not that slow to draw.
292 // center coordinates
293 int x
= (app
->screen
->get_width() - pm
.get_width()) / 2;
294 int y
= (app
->screen
->get_height() - pm
.get_height()) / 2;
295 Pixbuf
const *pb
= pm
.create_pixbuf_screenshot();
296 app
->screen
->blit_pixbuf(*pb
, x
, y
, false);
299 app
->screen
->drawing_finished();
303 void ReplaySaverActivity::timer2_event() {
304 /* iterate and see what happened */
305 /* give no gameinputhandler to the renderer */
306 GameRenderer::State state
= gamerenderer
.main_int(40, false, NULL
);
307 gamerenderer
.draw(pm
.must_redraw_all_before_flip());
310 case GameRenderer::Nothing
:
313 case GameRenderer::Stop
: /* game stopped, this could be a replay or a snapshot */
314 case GameRenderer::GameOver
: /* game over should not happen for a replay, but no problem */
315 app
->enqueue_command(new PopActivityCommand(app
));
319 /* before incrementing frame number, check if to save the frame to disk. */
320 pm
.save(CPrintf("%s_%08d.png") % filename_prefix
.c_str() % frame
);
326 #endif /* IFDEF HAVE_SDL */