CMathODE *SpanNew()
{
    CMathODE *ode = ODENew(0, 1e-4, ODEF_HAS_MASSES | ODEF_PAUSED);

    ode->derive             = &MyDerivative;
    ode->drag_v2            = 0.002;
    ode->drag_v3            = 0.00001;
    ode->acceleration_limit = 5e3;
    QueueInsert(ode, Fs->last_ode);

    return ode;
}

U0 SpanDel(CMathODE *ode)
{
    if (ode)
    {
        QueueRemove(ode);
        QueueDel(&ode->next_mass, TRUE);
        QueueDel(&ode->next_spring, TRUE);
        ODEDel(ode);
    }
}

#define M_SIZE (sizeof(MyMass)   - offset(CMass.start))
#define S_SIZE (sizeof(MySpring) - offset(CSpring.start))

U8 *SpanSave(CMathODE *ode, I64 *_size=NULL)
{
    I64          count;
    U8          *res, *ptr;
    MyMass      *tmpm;
    MySpring    *tmps;
    SpanHeader   h;

    ODERenum(ode);
    h.version = SPAN_VERSION;
    if (ode->next_mass != &ode->next_mass)
        h.num_masses = ode->last_mass->num + 1;
    else
        h.num_masses = 0;
    if (ode->next_spring != &ode->next_spring)
        h.num_springs = ode->last_spring->num + 1;
    else
        h.num_springs = 0;

    count = sizeof(SpanHeader) + h.num_masses * M_SIZE + h.num_springs * S_SIZE;

    ptr = res = MAlloc(count);
    MemCopy(ptr, &h, sizeof(SpanHeader));
    ptr += sizeof(SpanHeader);

    tmpm = ode->next_mass;
    while (tmpm != &ode->next_mass)
    {
        MemCopy(ptr, &tmpm->start, M_SIZE);
        ptr += M_SIZE;
        tmpm = tmpm->next;
    }

    tmps = ode->next_spring;
    while (tmps != &ode->next_spring)
    {
        MemCopy(ptr, &tmps->start, S_SIZE);
        ptr += S_SIZE;
        tmps = tmps->next;
    }
    if (_size)
        *_size = count;

    return res;
}

Bool SpanWrite(CMathODE *ode)
{
    U8  *name, *buf;
    I64  size;
    Bool res = FALSE, old_silent = Silent;

    DirMake("~/Span");
    Silent(old_silent);
    if (name = PopUpFileName("~/Span/Game.DATA"))
    {
        if (buf = SpanSave(ode, &size))
        {
            FileWrite(name, buf, size);
            Free(buf);
            res = TRUE;
        }
        Free(name);
    }

    return res;
}

U0 SpanLoad(CMathODE *ode, U8 *src)
{
    I64        i;
    MyMass    *tmpm;
    MySpring  *tmps;
    SpanHeader h;

    if (!src)
        return;

    MemCopy(&h, src, sizeof(SpanHeader));
    src += sizeof(SpanHeader);

    for (i = 0; i < h.num_masses; i++)
    {
        tmpm = CAlloc(sizeof(MyMass));
        MemCopy(&tmpm->start, src, M_SIZE);
        src += M_SIZE;
        QueueInsert(tmpm, ode->last_mass);
    }

    for (i = 0; i < h.num_springs; i++)
    {
        tmps = CAlloc(sizeof(MySpring));
        MemCopy(&tmps->start, src, S_SIZE);
        src += S_SIZE;
        QueueInsert(tmps, ode->last_spring);
        tmps->end1 = MassFindNum(ode, tmps->end1_num);
        tmps->end2 = MassFindNum(ode, tmps->end2_num);
    }
}

U8 *SpanRead()
{
    U8  *src = NULL, *name;
    Bool old_silent = Silent;

    DirMake("~/Span");
    Silent(old_silent);
    if (name = PopUpPickFile("~/Span"))
    {
        src = FileRead(name);
        Free(name);
    }

    return src;
}