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