signal_sl.cpp

Go to the documentation of this file.
00001 /* $Id$ */
00002 
00003 /*
00004  * This file is part of OpenTTD.
00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
00008  */
00009 
00012 #include "../stdafx.h"
00013 #include "../programmable_signals.h"
00014 #include "../core/alloc_type.hpp"
00015 #include "../core/bitmath_func.hpp"
00016 #include <vector>
00017 #include "saveload.h"
00018 
00019 typedef std::vector<byte> Buffer;
00020 
00021 /* Variable length integers are stored in Variable Length Quantity
00022  * format (http://en.wikipedia.org/wiki/Variable-length_quantity). */
00023 
00024 static void WriteVLI(Buffer &b, uint i)
00025 {
00026     uint lsmask =  0x7F;
00027     uint msmask = ~0x7F;
00028     while(i & msmask) {
00029         byte part = (i & lsmask) | 0x80;
00030         b.push_back(part);
00031         i >>= 7;
00032     }
00033     b.push_back((byte) i);
00034 }
00035 
00036 static uint ReadVLI()
00037 {
00038     uint shift = 0;
00039     uint val = 0;
00040     byte b;
00041 
00042     b = SlReadByte();
00043     while(b & 0x80) {
00044         val |= uint(b & 0x7F) << shift;
00045         shift += 7;
00046         b = SlReadByte();
00047     }
00048     val |= uint(b) << shift;
00049     return val;
00050 }
00051 
00052 static void WriteCondition(Buffer &b, SignalCondition *c)
00053 {
00054   WriteVLI(b, c->ConditionCode());
00055   switch(c->ConditionCode()) {
00056     case PSC_NUM_GREEN:
00057     case PSC_NUM_RED: {
00058       SignalVariableCondition *vc = static_cast<SignalVariableCondition*>(c);
00059       WriteVLI(b, vc->comparator);
00060       WriteVLI(b, vc->value);
00061     } break;
00062     
00063     case PSC_SIGNAL_STATE: {
00064       SignalStateCondition *sc = static_cast<SignalStateCondition*>(c);
00065       WriteVLI(b, sc->sig_tile);
00066       WriteVLI(b, sc->sig_track);
00067     } break;
00068     
00069     default:
00070       break;
00071   }
00072 }
00073 
00074 static SignalCondition *ReadCondition(SignalReference this_sig)
00075 {
00076   SignalConditionCode code = (SignalConditionCode) ReadVLI();
00077   switch(code) {
00078     case PSC_NUM_GREEN:
00079     case PSC_NUM_RED: {
00080       SignalVariableCondition *c = new SignalVariableCondition(code);
00081       c->comparator = (SignalComparator) ReadVLI();
00082       if(c->comparator > SGC_LAST) NOT_REACHED();
00083       c->value = ReadVLI();
00084       return c;
00085     }
00086     
00087     case PSC_SIGNAL_STATE: {
00088       TileIndex ti = (TileIndex) ReadVLI();
00089       Trackdir  td = (Trackdir) ReadVLI();
00090       return new SignalStateCondition(this_sig, ti, td);
00091     }
00092     
00093     default:
00094       return new SignalSimpleCondition(code);
00095   }
00096 }
00097 
00098 static void Save_SPRG()
00099 {
00100   /* Check for, and dispose of, any signal information on a tile which doesn't have signals.
00101    * This indicates that someone removed the signals from the tile but didn't clean them up.
00102    * (This code is to detect bugs and limit their consquences, not to cover them up!) */
00103   for(ProgramList::iterator i = _signal_programs.begin(), e = _signal_programs.end();
00104       i != e; ++i) {
00105     SignalReference ref = i->first;
00106     if(!HasProgrammableSignals(ref)) {
00107       DEBUG(sl, 0, "Programmable signal information for (%x, %d) has been leaked!",
00108             ref.tile, ref.track);
00109       ++i;
00110       FreeSignalProgram(ref);
00111       if(i == e) break;
00112     }
00113   }
00114   
00115   /* OK, we can now write out our programs. */
00116   Buffer b;
00117   WriteVLI(b, _signal_programs.size());
00118   for(ProgramList::iterator i = _signal_programs.begin(), e = _signal_programs.end();
00119       i != e; ++i) {
00120     SignalReference ref = i->first;
00121     SignalProgram *prog = i->second;
00122   
00123     prog->DebugPrintProgram();
00124   
00125     WriteVLI(b, prog->tile);
00126     WriteVLI(b, prog->track);
00127     WriteVLI(b, prog->instructions.Length());
00128     for(SignalInstruction **j = prog->instructions.Begin(), **je = prog->instructions.End();
00129         j != je; ++j) {
00130       SignalInstruction *insn = *j;
00131       WriteVLI(b, insn->Opcode());
00132       if(insn->Opcode() != PSO_FIRST)
00133         WriteVLI(b, insn->Previous()->Id());
00134       switch(insn->Opcode()) {
00135         case PSO_FIRST: {
00136           SignalSpecial *s = static_cast<SignalSpecial*>(insn);
00137           WriteVLI(b, s->next->Id());
00138           break;
00139         }
00140         
00141         case PSO_LAST: break;
00142         
00143         case PSO_IF: {
00144           SignalIf *i = static_cast<SignalIf*>(insn);
00145           WriteCondition(b, i->condition);
00146           WriteVLI(b, i->if_true->Id()); 
00147           WriteVLI(b, i->if_false->Id());
00148           WriteVLI(b, i->after->Id());
00149           break;
00150         }
00151         
00152         case PSO_IF_ELSE:
00153         case PSO_IF_ENDIF: {
00154           SignalIf::PseudoInstruction *p = static_cast<SignalIf::PseudoInstruction*>(insn);
00155           WriteVLI(b, p->block->Id());
00156           break;
00157         }
00158         
00159         case PSO_SET_SIGNAL: {
00160           SignalSet *s = static_cast<SignalSet*>(insn);
00161           WriteVLI(b, s->next->Id());
00162           WriteVLI(b, s->to_state ? 1 : 0);
00163           break;
00164         }
00165         
00166         default: NOT_REACHED();
00167       }
00168     }
00169   }
00170   
00171   uint size = b.size();
00172   SlSetLength(size);
00173   for(uint i = 0; i < size; i++)
00174     SlWriteByte(b[i]); // TODO Gotta be a better way
00175 }
00176 
00177 static void Load_SSIG()
00178 {
00179   _speedlimits.clear();
00180 
00181   uint count = ReadVLI();
00182   for(uint i = 0; i < count; i++) {
00183     TileIndex tile    = ReadVLI();
00184     Track     track   = (Track) ReadVLI();
00185     uint16 speed = ReadVLI();
00186     SignalReference ref(tile, track);
00187 
00188     _speedlimits[ref] = speed;
00189   }
00190 }
00191 
00192 static void Save_SSIG()
00193 {
00194   /* Check for, and dispose of, any signal information on a tile which doesn't have signals.
00195    * This indicates that someone removed the signals from the tile but didn't clean them up.
00196    * (This code is to detect bugs and limit their consquences, not to cover them up!) */
00197   for(SpeedLimits::iterator i = _speedlimits.begin(), e = _speedlimits.end();
00198       i != e; ++i) {
00199     SignalReference ref = i->first;
00200     if(!HasSpeedSignals(ref)) {
00201       DEBUG(sl, 0, "Speed signal information for (%x, %d) has been leaked!",
00202             ref.tile, ref.track);
00203       ++i;
00204       FreeSignalSpeed(ref);
00205       if(i == e) break;
00206     }
00207   }
00208 
00209   /* OK, we can now write out our speed limits. */
00210   Buffer b;
00211   WriteVLI(b, _speedlimits.size());
00212   for(SpeedLimits::iterator i = _speedlimits.begin(), e = _speedlimits.end();
00213       i != e; ++i) {
00214     SignalReference ref = i->first;
00215     uint16 speed = i->second;
00216 
00217     WriteVLI(b, ref.tile);
00218     WriteVLI(b, ref.track);
00219     WriteVLI(b, speed);
00220   }
00221 
00222   uint size = b.size();
00223   SlSetLength(size);
00224   for(uint i = 0; i < size; i++)
00225     SlWriteByte(b[i]); // TODO Gotta be a better way.
00226 }
00227 
00228 /* We don't know the pointer values that need to be stored in various 
00229  * instruction fields at load time, so we need to instead store the IDs and
00230  * then fix them up once all of the instructions have been loaded.
00231  *
00232  * Additionally, we store the opcode type we expect (if we expect a specific one)
00233  * to check for consistency (For example, an If Pseudo Instruction's block should
00234  * point at an If!) */
00235 struct Fixup {
00236   Fixup(SignalInstruction **p, SignalOpcode type)
00237     : type(type), ptr(p)
00238   {}
00239   
00240   SignalOpcode type;
00241   SignalInstruction **ptr;
00242 };
00243 
00244 typedef SmallVector<Fixup, 4> FixupList;
00245 
00246 template<typename T>
00247 static void MakeFixup(FixupList &l, T *&ir, uint id, SignalOpcode op = PSO_INVALID)
00248 {
00249   ir = reinterpret_cast<T*>(id);
00250   new(l.Append()) Fixup(reinterpret_cast<SignalInstruction**>(&ir), op);
00251 }
00252 
00253 static void DoFixups(FixupList &l, InstructionList &il)
00254 {
00255   for(Fixup *i = l.Begin(), *e = l.End(); i != e; ++i) {
00256     uint id = reinterpret_cast<size_t>(*i->ptr);
00257     if(id >= il.Length())
00258       NOT_REACHED();
00259       
00260     *i->ptr = il[id];
00261     
00262     if(i->type != PSO_INVALID && (*i->ptr)->Opcode() != i->type) {
00263       DEBUG(sl, 0, "Expected Id %d to be %d, but was in fact %d", id, i->type, (*i->ptr)->Opcode());
00264       NOT_REACHED();
00265     }
00266   }
00267 }
00268 
00269 static void Load_SPRG()
00270 {
00271   uint count = ReadVLI();
00272   for(uint i = 0; i < count; i++) {
00273     FixupList l;
00274     TileIndex tile    = ReadVLI();
00275     Track     track   = (Track) ReadVLI();
00276     uint instructions = ReadVLI();
00277     SignalReference ref(tile, track);
00278     
00279     SignalProgram *sp = new SignalProgram(tile, track, true);
00280     _signal_programs[ref] = sp;
00281     
00282     for(uint j = 0; j < instructions; j++) {
00283       SignalOpcode op = (SignalOpcode) ReadVLI();
00284       switch(op) {
00285         case PSO_FIRST: {
00286           sp->first_instruction = new SignalSpecial(sp, PSO_FIRST);
00287           sp->first_instruction->GetPrevHandle() = NULL;
00288           MakeFixup(l, sp->first_instruction->next, ReadVLI());
00289           break;
00290         }
00291         
00292         case PSO_LAST: {
00293           sp->last_instruction = new SignalSpecial(sp, PSO_LAST);
00294           sp->last_instruction->next = NULL;
00295           MakeFixup(l, sp->last_instruction->GetPrevHandle(), ReadVLI());
00296           break;
00297         }
00298         
00299         case PSO_IF: {
00300           SignalIf *i = new SignalIf(sp, true);
00301           MakeFixup(l, i->GetPrevHandle(), ReadVLI());
00302           i->condition = ReadCondition(ref);
00303           MakeFixup(l, i->if_true,  ReadVLI()); 
00304           MakeFixup(l, i->if_false, ReadVLI()); 
00305           MakeFixup(l, i->after, ReadVLI()); 
00306           break;
00307         }
00308         
00309         case PSO_IF_ELSE:
00310         case PSO_IF_ENDIF: {
00311           SignalIf::PseudoInstruction *p = new SignalIf::PseudoInstruction(sp, op);
00312           MakeFixup(l, p->GetPrevHandle(), ReadVLI());
00313           MakeFixup(l, p->block, ReadVLI(), PSO_IF);
00314           break;
00315         }
00316         
00317         case PSO_SET_SIGNAL: {
00318           SignalSet *s = new SignalSet(sp);
00319           MakeFixup(l, s->GetPrevHandle(), ReadVLI());
00320           MakeFixup(l, s->next, ReadVLI());
00321           s->to_state = (SignalState) ReadVLI();
00322           if(s->to_state > SIGNAL_STATE_MAX) NOT_REACHED();
00323           break;
00324         }
00325         
00326         default: NOT_REACHED();
00327       }
00328     }
00329     
00330     DoFixups(l, sp->instructions);
00331     sp->DebugPrintProgram();
00332   }
00333 }
00334 
00335 extern const ChunkHandler _signal_chunk_handlers[] = {
00336   { 'SPRG', Save_SPRG, Load_SPRG, NULL, NULL, CH_RIFF | CH_LAST},
00337 };
00338 
00339 extern const ChunkHandler _speed_signal_chunk_handlers[] = {
00340   { 'SSIG', Save_SSIG, Load_SSIG, NULL, NULL, CH_RIFF | CH_LAST},
00341 };