// Parse the serialized data into a Element struct // When the end of ride is encountered a EndOfRide error is returned // Documentation from // http://freerct.github.io/RCTTechDepot-Archive/trackQualifier.html func unmarshalElement(rawElement []byte) (te Element, e error) { if len(rawElement) != 2 { return Element{}, errors.New("invalid length for element input") } // XXX hack for brakes if rawElement[0] == 0xd8 { rawElement[0] = ELEM_BRAKES } te.Segment = TS_MAP[SegmentType(rawElement[0])] if te.Segment.Type == ELEM_END_OF_RIDE { return Element{}, EndOfRide } q := int(rawElement[1]) te.ChainLift = bits.On(q, 7) te.InvertedTrack = bits.On(q, 6) te.Station = bits.On(q, 3) if te.Station { fmt.Println("found station piece") fmt.Println(te.Segment) } te.StationNumber = q & 3 te.BoostMagnitude = float32(q&15) * 7.6 te.Rotation = (q&15)*45 - 180 return }
// XXX, this doesn't correctly handle s-bends, which only move sideways by 1 // piece, I think. func SidewaysDelta(sidewaysDeltaByte int) int { if sidewaysDeltaByte == 0 { return 0 } if bits.On(sidewaysDeltaByte, 7) { return 1 + (256-sidewaysDeltaByte)>>5 } return -(1 + sidewaysDeltaByte>>5) }
func testMarshalControlFlags(t *testing.T) { t.Parallel() cFlags := ControlFlags{ Load: 3, UseMaximumTime: true, } n := marshalControlFlags(cFlags) if !bits.On(n, 7) { t.Errorf("Maximum time bit should have been set, but wasn't, n is %d", n) } }
// Get list of entrances/exits for a ride from raw data // Copied mostly from "Scenery Items" here: // http://freerct.github.io/RCTTechDepot-Archive/TD6.html func unmarshalEgress(buf []byte) []*Egress { var egrs []*Egress for i := 0; true; i += 6 { if buf[i] == 0xff { break } features := int(buf[i+1]) egr := &Egress{ Exit: bits.On(features, 7), // Direction set in lower two bits Direction: features & 3, XOffset: int16(binary.LittleEndian.Uint16(buf[i+2 : i+4])), YOffset: int16(binary.LittleEndian.Uint16(buf[i+4 : i+6])), } egrs = append(egrs, egr) } return egrs }
func hasBit(n int, pos uint) bool { return bits.On(n, pos) }
func hasLoop(n int) bool { return bits.On(n, BIT_VERTICAL_LOOP) }
// Take a compressed byte stream representing a ride and turn it into a Ride // struct. Returns an error if the byte array is too short. func Unmarshal(buf []byte, r *Ride) error { if len(buf) < IDX_TRACK_DATA { return errors.New("buffer too short to be a ride") } r.RideType = RideType(buf[IDX_RIDE_TYPE]) featuresBit0 := int(buf[IDX_FEATURES_0]) r.SteepLiftChain = bits.On(featuresBit0, BIT_STEEP_LIFT_CHAIN) r.CurvedLiftChain = bits.On(featuresBit0, BIT_CURVED_LIFT_CHAIN) r.Banking = bits.On(featuresBit0, BIT_BANKING) r.HasLoop = bits.On(featuresBit0, BIT_VERTICAL_LOOP) featuresBit1 := int(buf[IDX_FEATURES_1]) r.SteepSlope = bits.On(featuresBit1, BIT_STEEP_SLOPE) r.FlatToSteep = bits.On(featuresBit1, BIT_FLAT_TO_STEEP) r.SlopedCurves = bits.On(featuresBit1, BIT_SLOPED_CURVES) r.SteepTwist = bits.On(featuresBit1, BIT_STEEP_TWIST) r.SBends = bits.On(featuresBit1, BIT_S_BENDS) r.SmallRadiusCurves = bits.On(featuresBit1, BIT_SMALL_RADIUS_CURVES) r.SmallRadiusBanked = bits.On(featuresBit1, BIT_SMALL_RADIUS_BANKED) r.OperatingMode = OperatingMode(buf[IDX_OPERATING_MODE]) r.ControlFlags = unmarshalControlFlags(int(buf[IDX_CONTROL_FLAG])) r.NumTrains = uint8(buf[IDX_NUM_TRAINS]) r.CarsPerTrain = uint8(buf[IDX_CARS_PER_TRAIN]) r.MinWaitTime = uint8(buf[IDX_MIN_WAIT_TIME]) r.MaxWaitTime = uint8(buf[IDX_MAX_WAIT_TIME]) r.NumInversions = uint8(buf[IDX_NUM_INVERSIONS]) r.MaxSpeed = uint8(buf[IDX_MAX_SPEED]) r.AverageSpeed = uint8(buf[IDX_AVERAGE_SPEED]) d := new(tracks.Data) tracks.Unmarshal(buf[IDX_TRACK_DATA:], d) r.TrackData = *d r.VehicleType = VehicleType(string(buf[IDX_VEHICLE_TYPE_STRING : IDX_VEHICLE_TYPE_STRING+LENGTH_VEHICLE_TYPE])) r.XSpaceRequired = int(buf[IDX_X_SPACE]) r.YSpaceRequired = int(buf[IDX_Y_SPACE]) r.DatData = buf[0x70:0x80] // XXX, not sure if this fn should know about this, or the track data entranceExitIdx := IDX_TRACK_DATA + 2*len(r.TrackData.Elements) + 1 r.Egresses = unmarshalEgress(buf[entranceExitIdx:]) r.Excitement = int16(buf[IDX_EXCITEMENT]) r.Intensity = int16(buf[IDX_INTENSITY]) r.Nausea = int16(buf[IDX_NAUSEA]) // Ignore scenery data for now. // sceneryIdx := entranceExitIdx + 6*len(r.Egresses) + 1 return nil }