# 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 };