1 /*
2  * The contents of this file are subject to the Mozilla Public License
3  * Version 1.0 (the "License"); you may not use this file except in
4  * compliance with the License. You may obtain a copy of the License at
5  * http://www.mozilla.org/MPL/
6  *
7  * Software distributed under the License is distributed on an "AS IS"
8  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
9  * License for the specific language governing rights and limitations
10  * under the License.
11  *
12  * The Initial Developer of this code is David Baum.
13  * Portions created by David Baum are Copyright (C) 1998 David Baum.
14  * All Rights Reserved.
15  *
16  * Portions created by John Hansen are Copyright (C) 2005 John Hansen.
17  * All Rights Reserved.
18  *
19  */
20 
21 #include <cstring>
22 #include "RCX_SpyboticsLinker.h"
23 #include "RCX_Image.h"
24 #include "RCX_Target.h"
25 #include "PDebug.h"
26 
27 using std::memcpy;
28 
29 static const RCX_ChunkType sChunkOrder[] = {
30 	kRCX_SubChunk,
31 	kRCX_TaskChunk,
32 	kRCX_SoundChunk,
33 	kRCX_AnimationChunk
34 };
35 
36 
37 struct EmptyChunkDef
38 {
39 	UByte	fLength;
40 	UByte	fData[3];	// max of 3 bytes
41 };
42 
43 static const EmptyChunkDef sEmptyChunkDefs[] =
44 {
45 	{ 0 },			// empty task (not used - tasks are special cases)
46 	{ 1, 0xf6 },	// empty sub
47 	{ 1, 0 }, 		// empty sound
48 	{ 2, 0, 0 }, 	// empty animation
49 };
50 
51 
RCX_SpyboticsLinker()52 RCX_SpyboticsLinker::RCX_SpyboticsLinker()
53 {
54 	int i;
55 
56 	fTarget = getTarget(kRCX_SpyboticsTarget);
57 
58 	// clear the section start array
59 	for(i=0; i<kRCX_ChunkTypeCount; ++i) fSectionStarts[i] = -1;
60 
61 	// allocate slots
62 	fSlotCount = 0;
63 	for(i=0; i<sizeof(sChunkOrder) / sizeof(RCX_ChunkType); ++i)
64 	{
65 		fSlotCount += fTarget->fRanges[sChunkOrder[i]].fCount;
66 	}
67 	fSlots = new Slot[fSlotCount];
68 
69 	// initialize slots
70 	int index = 0;
71 	for(i=0; i<sizeof(sChunkOrder) / sizeof(RCX_ChunkType); ++i)
72 	{
73 		RCX_ChunkType type = sChunkOrder[i];
74 		fSectionStarts[type] = index;
75 
76 		int end = index + fTarget->fRanges[type].fCount;
77 		while(index < end)
78 		{
79 			fSlots[index].fType = type;
80 			fSlots[index].fChunk = 0;
81 			++index;
82 		}
83 	}
84 }
85 
86 
~RCX_SpyboticsLinker()87 RCX_SpyboticsLinker::~RCX_SpyboticsLinker()
88 {
89 	delete [] fSlots;
90 }
91 
92 
Generate(const RCX_Image & image,std::vector<UByte> & output)93 void RCX_SpyboticsLinker::Generate(const RCX_Image &image, std::vector<UByte> &output)
94 {
95 	int tocSize = (fSlotCount+2) * 2;
96 
97 	fTocOffset = 0;
98 	fContentOffset = tocSize;
99 	fOutput = &output;
100 	fOutput->reserve(tocSize + 1000);
101 	fOutput->resize(fContentOffset);
102 
103 	PlaceChunks(image);
104 	GenerateSlots();
105 
106 	// data area
107 	AddEntry();
108 
109 	// end marker
110 	AddEntry();
111 
112 
113 	// dump the binary image for debugging
114 /*
115 	int length = fOutput->size();
116 	const UByte* data = &(*fOutput)[0];
117 	for(int i=0; i<length; ++i) {
118 		if ((i%16)==0) printf("\n%04x: ", i);
119 		printf("%02x ", data[i]);
120 	}
121 	printf("\n");
122 /**/
123 
124 }
125 
126 
PlaceChunks(const RCX_Image & image)127 void RCX_SpyboticsLinker::PlaceChunks(const RCX_Image &image)
128 {
129 	int i;
130 
131 	for(i=0; i<image.GetChunkCount(); ++i)
132 	{
133 		const RCX_Image::Chunk &c = image.GetChunk(i);
134 		RCX_ChunkType type = c.GetType();
135 
136 		PASSERT(type >= 0 && type < kRCX_ChunkTypeCount);
137 		PASSERT(fSectionStarts[type] >=0);
138 
139 		// compute slot index
140 		int index = c.GetNumber() - fTarget->fRanges[type].fBase + fSectionStarts[type];
141 
142 		PASSERT(index>=0 && index<fSlotCount);
143 		PASSERT(fSlots[index].fType == type);
144 
145 		// add chunk to index
146 		fSlots[index].fChunk = &c;
147 	}
148 }
149 
150 
GenerateSlots()151 void RCX_SpyboticsLinker::GenerateSlots()
152 {
153 	for(int i=0; i<fSlotCount; ++i)
154 	{
155 		const RCX_Image::Chunk* c = fSlots[i].fChunk;
156 
157 		if (c)
158 		{
159 			AddEntry(c->GetData(), c->GetLength());
160                         // end non-empty subroutines with rets (0xF6)
161                         if (fSlots[i].fType == kRCX_SubChunk)
162                         {
163                                 UByte rets[1] = {0xF6};
164                                 AddData(rets, 1);
165                         }
166 		}
167 		else
168 		{
169 			RCX_ChunkType type = fSlots[i].fType;
170 
171 			const EmptyChunkDef &def = sEmptyChunkDefs[type];
172 			AddEntry(def.fData, def.fLength);
173 
174 			// only add endTask for empty tasks
175 			if (fSlots[i].fType == kRCX_TaskChunk)
176 			{
177 				UByte endTask[2] = {0x81, 0};
178                                 // The MindScript compiler does not offset
179                                 // task numbers by 8
180 				endTask[1] = i - fSectionStarts[kRCX_TaskChunk]/*+8*/;
181 				AddData(endTask, 2);
182 			}
183 		}
184 	}
185 }
186 
187 
AddEntry(const UByte * data,int length)188 void RCX_SpyboticsLinker::AddEntry(const UByte *data, int length)
189 {
190 	// add TOC entry
191 	int address = fContentOffset + 0x8100;
192 	(*fOutput)[fTocOffset++] = (address) & 0xff;
193 	(*fOutput)[fTocOffset++] = (address >> 8) & 0xff;
194 
195 	if (length) AddData(data, length);
196 }
197 
198 
AddData(const UByte * data,int length)199 void RCX_SpyboticsLinker::AddData(const UByte *data, int length)
200 {
201 	if (!length) return;
202 
203 	// add data
204 	fOutput->resize(fContentOffset + length);
205 	memcpy(&(*fOutput)[fContentOffset], data, length);
206 	fContentOffset += length;
207 }
208