Rev 3408 |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
/*
* Copyright (C) 2009.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* This program 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. See the GNU
* General Public License for more details.
*/
package uk.me.parabola.imgfmt.app.mdr;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import uk.me.parabola.imgfmt.app.ImgFileWriter;
import uk.me.parabola.imgfmt.app.srt.MultiSortKey;
import uk.me.parabola.imgfmt.app.srt.Sort;
import uk.me.parabola.imgfmt.app.srt.SortKey;
/**
* Section containing cities.
*
* We need: map number, city index in map, offset in LBL, flags
* and pointer into MDR 15 for the string name.
*
* @author Steve Ratcliffe
*/
public class Mdr5
extends MdrMapSection
{
private List<Mdr5Record
> allCities =
new ArrayList<>();
private List<Mdr5Record
> cities =
new ArrayList<>();
private int maxCityIndex
;
private int localCitySize
;
public Mdr5
(MdrConfig config
) {
setConfig
(config
);
}
public void addCity
(Mdr5Record record
) {
assert record.
getMapIndex() !=
0;
allCities.
add(record
);
if (record.
getCityIndex() > maxCityIndex
)
maxCityIndex = record.
getCityIndex();
}
/**
* Called after all cities to sort and number them.
*/
public void preWriteImpl
() {
localCitySize = numberToPointerSize
(maxCityIndex +
1);
List<SortKey<Mdr5Record
>> sortKeys =
new ArrayList<>(allCities.
size());
Sort sort = getConfig
().
getSort();
for (Mdr5Record m : allCities
) {
if (m.
getName() ==
null)
continue;
// Sort by city name, region name, country name and map index.
SortKey<Mdr5Record
> sortKey = sort.
createSortKey(m, m.
getName());
SortKey<Mdr5Record
> regionKey = sort.
createSortKey(null, m.
getRegionName());
SortKey<Mdr5Record
> countryKey = sort.
createSortKey(null, m.
getCountryName(), m.
getMapIndex());
sortKey =
new MultiSortKey
<>(sortKey, regionKey, countryKey
);
sortKeys.
add(sortKey
);
}
Collections.
sort(sortKeys
);
Collator collator = getConfig
().
getSort().
getCollator();
int count =
0;
Mdr5Record lastCity =
null;
// We need a common area to save the mdr20 values, since there can be multiple
// city records with the same global city index
int[] mdr20s =
new int[sortKeys.
size()+
1];
int mdr20count =
0;
for (SortKey<Mdr5Record
> key : sortKeys
) {
Mdr5Record c = key.
getObject();
c.
setMdr20set(mdr20s
);
if (!c.
isSameByName(collator, lastCity
))
mdr20count++
;
c.
setMdr20Index(mdr20count
);
if (c.
isSameByMapAndName(collator, lastCity
)) {
c.
setGlobalCityIndex(count
);
} else {
count++
;
c.
setGlobalCityIndex(count
);
cities.
add(c
);
lastCity = c
;
}
}
}
public void writeSectData
(ImgFileWriter writer
) {
int size20 = getSizes
().
getMdr20Size();
Mdr5Record lastCity =
null;
boolean hasString = hasFlag
(0x8
);
boolean hasRegion = hasFlag
(0x4
);
Collator collator = getConfig
().
getSort().
getCollator();
for (Mdr5Record city : cities
) {
int gci = city.
getGlobalCityIndex();
addIndexPointer
(city.
getMapIndex(), gci
);
// Work out if the name is the same as the previous one and set
// the flag if so.
int flag =
0;
int mapIndex = city.
getMapIndex();
int region = city.
getRegionIndex();
// Set the no-repeat flag if the name/region is different
if (!city.
isSameByName(collator, lastCity
)) {
flag = 0x800000
;
lastCity = city
;
}
// Write out the record
putMapIndex
(writer, mapIndex
);
putLocalCityIndex
(writer, city.
getCityIndex());
writer.
put3(flag | city.
getLblOffset());
if (hasRegion
)
writer.
putChar((char) region
);
if (hasString
)
putStringOffset
(writer, city.
getStringOffset());
putN
(writer, size20, city.
getMdr20());
}
}
/**
* Put the map city index. This is the index within the individual map
* and not the global city index used in mdr11.
*/
private void putLocalCityIndex
(ImgFileWriter writer,
int cityIndex
) {
if (localCitySize ==
2) // 3 probably not possible in actual maps.
writer.
putChar((char) cityIndex
);
else
writer.
put((byte) cityIndex
);
}
/**
* Base size of 8, plus enough bytes to represent the map number
* and the city number.
* @return The size of a record in this section.
*/
public int getItemSize
() {
PointerSizes sizes = getSizes
();
int size = sizes.
getMapSize()
+ localCitySize
+
3
+ sizes.
getMdr20Size();
if (hasFlag
(0x4
))
size +=
2;
if (hasFlag
(0x8
))
size += sizes.
getStrOffSize();
return size
;
}
protected int numberOfItems
() {
return cities.
size();
}
/**
* Known structure:
* bits 0-1: size of local city index - 1 (all values appear to work)
* bit 3: has region
* bit 4: has string
* @return The value to be placed in the header.
*/
public int getExtraValue
() {
int val =
(localCitySize -
1);
// String offset is only included for a mapsource index.
if (isForDevice
()) {
val |= 0x40
; // not known, probably refers to mdr17.
} else {
val |= 0x04
; // region
val |= 0x08
; // string
}
val |= 0x10
;
val |= 0x100
; // mdr20 present
return val
;
}
protected void releaseMemory
() {
allCities =
null;
cities =
null;
}
/**
* Get a list of all the cities, including duplicate named ones.
* @return All cities.
*/
public List<Mdr5Record
> getCities
() {
return Collections.
unmodifiableList(allCities
);
}
public List<Mdr5Record
> getSortedCities
() {
return Collections.
unmodifiableList(cities
);
}
public void relabelMaps
(Mdr1 maps
) {
relabel
(maps, cities
);
}
}