Salting is nice, but when the attacker gets both the hash and the salt, they can attack specific users.
Of course they can. The entire purpose of salting is to make it so that the same password, hashed two different times, produces completely different hashes. This has two important consequences. First, it makes it basically impossible to precompute password hashes. That's a big deal compared to the "without salt" case, where rainbow tables make checking against precomputed hashes very easy. Second, if two users on a system have the same password, you can't tell without computation. Said another way, it means you need to crack passwords individually rather than in bulk. This isn't game-breaking, but it's significant when you have million-user breaches.
All of the typical ways of storing password hashes store the salt alongside it. It's expected that an attacker that obtains the hash will obtain the salt. It's within the design.
If you want the password hash separate from a piece of key password-validation data, at that point the extra piece of data is a secret and what you're basically making is a message authentication code. But, it's very difficult to argue that this is ever really more secure.
Still, the 100k rounds of SHA256 seem decent.
Would bcrypt be any better than PBKDF2 here?
I don't know that bcrypt is necessarily much better than what they're doing. It may be, but at a "details" level, not a "major benefit" level. Both bcrypt and PBKDF2 support many rounds and prevent precomputation, which are major features.
What would be better, if the devices they want to support can run it, is something like scrypt, which is resistant to hardware acceleration and thus much harder to crack in practice.