Add ability to edit game name from UI
[jpcrr.git] / streamtools / picturestodump.cpp
blob4731d9e692d60837c15e0b2f1ba3debe2030ccc5
1 #include "SDL_image.h"
2 #include "SDL.h"
3 #include "png-out.hpp"
4 #include <zlib.h>
5 #include <cstdlib>
6 #include <stdint.h>
7 #include <string>
8 #include <iostream>
9 #include <vector>
10 #include "newpacket.hpp"
11 #include "timecounter.hpp"
12 #include <stdexcept>
14 timecounter curtime(60);
15 std::vector<std::string> inputfiles;
16 std::string outputfile;
17 bool always_file = false;
19 void add_file(const std::string& file)
21 if(outputfile != "")
22 inputfiles.push_back(outputfile);
23 outputfile = file;
26 void usage()
28 std::cerr << "Syntax: picturestodump.exe <options> <input>... <output>" << std::endl;
29 std::cerr << "--fps=<fps>\tSet framerate to <fps>. Default 60." << std::endl;
30 std::cerr << "<input>...\tInput files, in order." << std::endl;
31 std::cerr << "<output>\t\tOutput file." << std::endl;
32 exit(1);
35 void resize_block(unsigned char*& block, size_t& alloc, size_t min = 0)
37 size_t new_size = 3 * alloc / 2 + 5;
38 if(new_size < min)
39 new_size = min;
40 block = (unsigned char*)realloc(block, new_size);
41 if(!block) {
42 std::cerr << "Out of memory while trying to allocate " << new_size << " bytes." << std::endl;
43 exit(2);
45 alloc = new_size;
48 void reset_zlib_output(z_stream* s, unsigned char* buffer, size_t buffersize)
50 s->next_out = buffer;
51 s->avail_out = buffersize;
54 void flush_zlib_output(z_stream* s, std::vector<unsigned char>& block, unsigned char* buffer, size_t buffersize)
56 //Resize the block so the new data fits.
57 size_t delta = buffersize - s->avail_out;
58 if(delta) {
59 size_t osize = block.size();
60 block.resize(osize + delta);
61 //Copy the data.
62 memcpy(&block[osize], buffer, delta);
64 //Reset the buffer.
65 s->next_out = buffer;
66 s->avail_out = buffersize;
69 #define INBUFFER_SIZE 65536
70 #define OUTBUFFER_SIZE 16384
72 void write_image(const std::string& name, write_channel& wchan)
74 struct packet p;
75 uint32_t width = 0;
76 uint32_t height = 0;
77 uint8_t* pixels;
78 p.rp_channel = 0; //On video channel.
79 p.rp_minor = 1; //Compressed frame.
80 p.rp_timestamp = curtime; //time.
81 //rp_payload empty.
83 //Load the image.
84 SDL_Surface* img = IMG_Load(name.c_str());
85 if(!img) {
86 std::cerr << "Can't load image '" << name << "':" << IMG_GetError() << std::endl;
87 exit(2);
90 //Convert the SDL surface into raw image.
91 width = img->w;
92 height = img->h;
93 pixels = new uint8_t[4 * width * height];
94 SDL_LockSurface(img);
95 for(uint32_t y = 0; y < height; y++)
96 for(uint32_t x = 0; x < width; x++) {
97 Uint8 r, g, b;
98 size_t addr = y * img->pitch + x * img->format->BytesPerPixel;
99 SDL_GetRGB(*(Uint32*)((unsigned char*)img->pixels + addr), img->format, &r, &g, &b);
100 pixels[4 * width * y + 4 * x + 0] = r;
101 pixels[4 * width * y + 4 * x + 1] = g;
102 pixels[4 * width * y + 4 * x + 2] = b;
103 pixels[4 * width * y + 4 * x + 3] = 0;
105 SDL_UnlockSurface(img);
106 SDL_FreeSurface(img);
108 //Reserve space for dimensions and write them.
109 p.rp_payload.push_back((width >> 8) & 0xFF);
110 p.rp_payload.push_back(width & 0xFF);
111 p.rp_payload.push_back((height >> 8) & 0xFF);
112 p.rp_payload.push_back(height & 0xFF);
114 //Compress the image data.
115 size_t bytes_processed = 0;
116 size_t bytes_total = 4 * width * height;
117 unsigned char in[INBUFFER_SIZE];
118 unsigned char out[OUTBUFFER_SIZE];
119 bool finished = false;
120 z_stream s;
122 memset(&s, 0, sizeof(z_stream));
123 int r = deflateInit(&s, 9);
124 if(r) {
125 std::cerr << "Zlib error: " << s.msg << " while initializing deflate." << std::endl;
126 exit(3);
129 reset_zlib_output(&s, out, OUTBUFFER_SIZE);
130 while(!finished) {
131 int flushflag = Z_NO_FLUSH;
133 if(s.avail_in == 0) {
134 //Fill the input buffer.
135 s.next_in = in;
136 if(INBUFFER_SIZE > bytes_total - bytes_processed) {
137 memcpy(in, pixels + bytes_processed, bytes_total - bytes_processed);
138 s.avail_in = bytes_total - bytes_processed;
139 bytes_processed = bytes_total;
140 } else {
141 memcpy(in, pixels + bytes_processed, INBUFFER_SIZE);
142 s.avail_in = INBUFFER_SIZE;
143 bytes_processed += INBUFFER_SIZE;
147 //Set the finish flag if needed.
148 if(bytes_processed == bytes_total)
149 flushflag = Z_FINISH;
150 //Compress the data.
151 r = deflate(&s, flushflag);
152 if(r < 0) {
153 if(s.msg)
154 std::cerr << "Zlib error: " << s.msg << " while deflating data." << std::endl;
155 exit(3);
157 if(r == Z_STREAM_END)
158 finished = true;
159 flush_zlib_output(&s, p.rp_payload, out, OUTBUFFER_SIZE);
161 deflateEnd(&s);
163 //Give the payload, write and free the payload.
164 wchan.write(p);
165 delete[] pixels;
168 int real_main(int argc, char** argv)
170 if(SDL_Init(0) < 0) {
171 std::cerr << "Can't initialize SDL." << std::endl;
172 exit(2);
174 IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF);
176 for(int i = 1; i < argc; i++) {
177 if(!always_file && !strcmp(argv[i], "--"))
178 always_file = true;
179 else if(!always_file && !strncmp(argv[i], "--fps=", 6))
180 try {
181 curtime = timecounter(argv[i] + 6);
182 } catch(std::exception& e) {
183 std::cerr << "Invalid --fps value: " << e.what() << std::endl;
184 exit(1);
186 else if(!always_file && !strncmp(argv[i], "--", 2))
187 usage();
188 else
189 add_file(std::string(argv[i]));
191 if(inputfiles.size() == 0)
192 usage();
194 std::vector<channel> channels;
195 channels.resize(2);
196 channels[0].c_channel = 0; //Channel #0.
197 channels[0].c_type = 0; //Video channel.
198 channels[0].c_channel_name = "<VIDEO>"; //Channel name.
199 channels[1].c_channel = 1; //Channel #1.
200 channels[1].c_type = 3; //Comment channel.
201 channels[1].c_channel_name = "<DUMMY>"; //Channel name.
203 //Open the dump and start new segment.
204 write_channel wchan(outputfile.c_str());
205 wchan.start_segment(channels);
207 //For each image...
208 for(std::vector<std::string>::iterator i = inputfiles.begin(); i != inputfiles.end(); ++i) {
209 write_image(*i, wchan);
210 curtime++;
213 //Closing comment packet and end stream.
214 struct packet p;
215 p.rp_channel = 1; //On comment channel.
216 p.rp_minor = 0; //Ignored.
217 p.rp_timestamp = curtime; //Ending time.
218 //Payload defaults to empty.
219 wchan.write(p);
220 return 0;