1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003-2013 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 // This class handles VEF files of BG2/ToB it is closely related to VVC (ScriptedAnimation)
22 
23 #include "VEFObject.h"
24 
25 #define YESNO(x) ( (x)?"Yes":"No")
26 
27 #include "Game.h"
28 #include "GameData.h"
29 #include "Interface.h"
30 #include "ScriptedAnimation.h"
31 #include "TableMgr.h"
32 #include "Video.h"
33 #include "System/DataStream.h"
34 
35 namespace GemRB {
36 
VEFObject()37 VEFObject::VEFObject()
38 {
39 	ResName[0]=0;
40 	SingleObject=false;
41 }
42 
VEFObject(ScriptedAnimation * sca)43 VEFObject::VEFObject(ScriptedAnimation *sca)
44 {
45 	Pos = sca->Pos;
46 	strnlwrcpy(ResName, sca->ResName, 8);
47 	SingleObject=true;
48 	ScheduleEntry entry;
49 	entry.start = core->GetGame()->GameTime;
50 	if (sca->Duration==0xffffffff) entry.length = 0xffffffff;
51 	else entry.length = sca->Duration+entry.start;
52 	entry.offset = Point(0,0);
53 	entry.type = VEF_VVC;
54 	entry.ptr = sca;
55 	memcpy(entry.resourceName, sca->ResName, sizeof(ieResRef) );
56 	entries.push_back(entry);
57 }
58 
~VEFObject()59 VEFObject::~VEFObject()
60 {
61 	Init();
62 }
63 
Init()64 void VEFObject::Init()
65 {
66 	for(auto& entry : entries) {
67 		if (!entry.ptr) continue;
68 		switch(entry.type) {
69 			case VEF_BAM:
70 			case VEF_VVC:
71 				delete (ScriptedAnimation *)entry.ptr;
72 				break;
73 			case VEF_VEF:
74 			case VEF_2DA:
75 				delete (VEFObject *)entry.ptr;
76 				break;
77 			default:; //error, no suitable destructor
78 		}
79 	}
80 }
81 
AddEntry(const ieResRef res,ieDword st,ieDword len,Point pos,ieDword type,ieDword gtime)82 void VEFObject::AddEntry(const ieResRef res, ieDword st, ieDword len, Point pos, ieDword type, ieDword gtime)
83 {
84 	ScheduleEntry entry;
85 
86 	memcpy(entry.resourceName, res, sizeof(ieResRef) );
87 	entry.start = gtime+st;
88 	if (len!=0xffffffff) len+=entry.start;
89 	entry.length = len;
90 	entry.offset = pos;
91 	entry.type = type;
92 	entry.ptr = NULL;
93 	entries.push_back(entry);
94 }
95 
CreateCell(const ieResRef res,ieDword start,ieDword end)96 ScriptedAnimation *VEFObject::CreateCell(const ieResRef res, ieDword start, ieDword end)
97 {
98 	ScriptedAnimation *sca = gamedata->GetScriptedAnimation( res, false);
99 	if (sca && end!=0xffffffff) {
100 		sca->SetDefaultDuration(AI_UPDATE_TIME*(end-start) );
101 	}
102 	return sca;
103 }
104 
CreateObject(const ieResRef res,SClass_ID id)105 VEFObject *VEFObject::CreateObject(const ieResRef res, SClass_ID id)
106 {
107 	if (gamedata->Exists( res, id, true) ) {
108 		VEFObject *obj = new VEFObject();
109 
110 		if (id==IE_2DA_CLASS_ID) {
111 			obj->Load2DA(res);
112 		} else {
113 			DataStream* stream = gamedata->GetResource(res, id);
114 			strnlwrcpy(obj->ResName, res, 8);
115 			obj->LoadVEF(stream);
116 		}
117 		return obj;
118 	}
119 	return NULL;
120 }
121 
UpdateDrawingState(int orientation)122 bool VEFObject::UpdateDrawingState(int orientation)
123 {
124 	drawQueue.clear();
125 	ieDword GameTime = core->GetGame()->GameTime;
126 	for (auto& entry : entries) {
127 		//don't render the animation if it is outside of the cycle
128 		if (entry.start > GameTime) continue;
129 		if (entry.length < GameTime) continue;
130 
131 		if (!entry.ptr) {
132 			switch(entry.type) {
133 				case VEF_2DA: //original gemrb implementation of composite video effects
134 					entry.ptr = CreateObject(entry.resourceName, IE_2DA_CLASS_ID);
135 					if (entry.ptr) {
136 						break;
137 					}
138 					// fall back to VEF
139 					// intentional fallthrough
140 				case VEF_VEF: //vanilla engine implementation of composite video effects
141 					entry.ptr = CreateObject(entry.resourceName, IE_VEF_CLASS_ID);
142 					if (entry.ptr ) {
143 						break;
144 					}
145 					// fall back to BAM or VVC
146 					// intentional fallthrough
147 				case VEF_BAM: //just a BAM
148 				case VEF_VVC: //videocell (can contain a BAM)
149 					entry.ptr = CreateCell(entry.resourceName, entry.length, entry.start);
150 					break;
151 				default:;
152 			}
153 		}
154 
155 		if (!entry.ptr) entry.type = VEF_INVALID;
156 
157 		bool ended = true;
158 		switch(entry.type) {
159 		case VEF_BAM:
160 		case VEF_VVC:
161 			ended = ((ScriptedAnimation *) entry.ptr)->UpdateDrawingState(orientation);
162 			break;
163 		case VEF_2DA:
164 		case VEF_VEF:
165 			ended = ((VEFObject *) entry.ptr)->UpdateDrawingState(orientation);
166 			break;
167 		}
168 
169 		if (ended) return true;
170 
171 		drawQueue.push_back(entry);
172 	}
173 	return false;
174 }
175 
Draw(const Region & vp,const Color & p_tint,int height,BlitFlags flags) const176 void VEFObject::Draw(const Region &vp, const Color &p_tint, int height, BlitFlags flags) const
177 {
178 	for (const auto& entry : drawQueue) {
179 		switch (entry.type) {
180 		case VEF_BAM:
181 		case VEF_VVC:
182 			((ScriptedAnimation *)entry.ptr)->Draw(vp, p_tint, height, flags);
183 			break;
184 		case VEF_2DA:
185 		case VEF_VEF:
186 			((VEFObject *)entry.ptr)->Draw(vp, p_tint, height, flags);
187 			break;
188 		}
189 	}
190 }
191 
Load2DA(const ieResRef resource)192 void VEFObject::Load2DA(const ieResRef resource)
193 {
194 	Init();
195 	AutoTable tab(resource);
196 
197 	if (!tab) {
198 		return;
199 	}
200 	SingleObject = false;
201 	strnlwrcpy(ResName, resource, 8);
202 	ieDword GameTime = core->GetGame()->GameTime;
203 	int rows = tab->GetRowCount();
204 	while(rows--) {
205 		Point offset;
206 		int delay, duration;
207 		ieResRef resource;
208 
209 		offset.x=atoi(tab->QueryField(rows,0));
210 		offset.y=atoi(tab->QueryField(rows,1));
211 		delay = atoi(tab->QueryField(rows,3));
212 		duration = atoi(tab->QueryField(rows,4));
213 		strnuprcpy(resource, tab->QueryField(rows,2), 8);
214 		AddEntry(resource, delay, duration, offset, VEF_VVC, GameTime);
215 	}
216 }
217 
ReadEntry(DataStream * stream)218 void VEFObject::ReadEntry(DataStream *stream)
219 {
220 	ieDword start;
221 	ieDword tmp;
222 	ieDword length;
223 	ieResRef resource;
224 	ieDword type;
225 	ieDword continuous;
226 	Point position;
227 
228 	stream->ReadDword( &start);
229 	position.x = 0;
230 	position.y = 0;
231 	stream->ReadDword( &tmp); //unknown field (could be position?)
232 	stream->ReadDword( &length);
233 	stream->ReadDword( &type);
234 	stream->ReadResRef( resource);
235 	stream->ReadDword( &continuous);
236 	stream->Seek( 49*4, GEM_CURRENT_POS); //skip empty fields
237 
238 	if (continuous) length = -1;
239 	ieDword GameTime = core->GetGame()->GameTime;
240 	AddEntry(resource, start, length, position, type, GameTime);
241 }
242 
LoadVEF(DataStream * stream)243 void VEFObject::LoadVEF(DataStream *stream)
244 {
245 	Init();
246 	if (!stream) {
247 		return;
248 	}
249 	ieDword i;
250 	ieResRef Signature;
251 	ieDword offset1, offset2;
252 	ieDword count1, count2;
253 
254 	stream->ReadResRef( Signature);
255 	if (strncmp( Signature, "VEF V1.0", 8 ) != 0) {
256 		Log(ERROR, "VEFObject", "Not a valid VEF File: %s", ResName);
257 		delete stream;
258 		return;
259 	}
260 	SingleObject = false;
261 	stream->ReadDword( &offset1);
262 	stream->ReadDword( &count1);
263 	stream->ReadDword( &offset2);
264 	stream->ReadDword( &count2);
265 
266 	stream->Seek(offset1, GEM_STREAM_START);
267 	for (i=0;i<count1;i++) {
268 		ReadEntry(stream);
269 	}
270 
271 	stream->Seek(offset2, GEM_STREAM_START);
272 	for (i=0;i<count2;i++) {
273 		ReadEntry(stream);
274 	}
275 }
276 
GetSingleObject() const277 ScriptedAnimation *VEFObject::GetSingleObject() const
278 {
279 	ScriptedAnimation *sca = NULL;
280 
281 	if (SingleObject) {
282 		if (entries.size()) {
283 			const ScheduleEntry& entry = entries[0];
284 			if (entry.type==VEF_VVC || entry.type==VEF_BAM) {
285 				sca = (ScriptedAnimation *)entry.ptr;
286 			}
287 		}
288 	}
289 	return sca;
290 }
291 
292 }
293