@Override
	public List<Trainer> getTrainers() {
		int traineroffset = romEntry.getValue("TrainerDataTableOffset");
		int traineramount = romEntry.getValue("TrainerClassAmount");
		int[] trainerclasslimits = romEntry.arrayEntries
				.get("TrainerDataClassCounts");

		int[] pointers = new int[traineramount + 1];
		for (int i = 1; i <= traineramount; i++) {
			int pointer = readWord(traineroffset + (i - 1) * 2);
			pointers[i] = calculateOffset(bankOf(traineroffset), pointer);
		}

		List<String> tcnames = this.getTrainerClassNames();

		List<Trainer> allTrainers = new ArrayList<Trainer>();
		for (int i = 1; i <= traineramount; i++) {
			int offs = pointers[i];
			int limit = trainerclasslimits[i];
			for (int trnum = 0; trnum < limit; trnum++) {
				Trainer tr = new Trainer();
				tr.offset = offs;
				tr.trainerclass = i;
				String name = readVariableLengthString(offs);
				tr.name = name;
				tr.fullDisplayName = tcnames.get(i - 1) + " " + name;
				int len = lengthOfStringAt(offs);
				offs += len + 1;
				int dataType = rom[offs] & 0xFF;
				tr.poketype = dataType;
				offs++;
				while ((rom[offs] & 0xFF) != 0xFF) {
					TrainerPokemon tp = new TrainerPokemon();
					tp.level = rom[offs] & 0xFF;
					tp.pokemon = pokes[rom[offs + 1] & 0xFF];
					offs += 2;
					if (dataType == 2 || dataType == 3) {
						tp.heldItem = rom[offs] & 0xFF;
						offs++;
					}
					if (dataType % 2 == 1) {
						tp.move1 = rom[offs] & 0xFF;
						tp.move2 = rom[offs + 1] & 0xFF;
						tp.move3 = rom[offs + 2] & 0xFF;
						tp.move4 = rom[offs + 3] & 0xFF;
						offs += 4;
					}
					tr.pokemon.add(tp);
				}
				allTrainers.add(tr);
				offs++;
			}
		}
		universalTrainerTags(allTrainers);
		if (romEntry.isCrystal) {
			crystalTags(allTrainers);
		} else {
			goldSilverTags(allTrainers);
		}
		return allTrainers;
	}